@cdot65/prisma-airs-sdk 0.13.0 → 0.13.2
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/index.cjs +141 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -30
- package/dist/index.d.ts +87 -30
- package/dist/index.js +141 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -589,11 +589,11 @@ var MAX_REPORT_ID_STR_LENGTH = 40;
|
|
|
589
589
|
var MAX_AI_PROFILE_NAME_LENGTH = 100;
|
|
590
590
|
var MAX_NUMBER_OF_SCAN_IDS = 5;
|
|
591
591
|
var MAX_NUMBER_OF_REPORT_IDS = 5;
|
|
592
|
-
var MAX_NUMBER_OF_BATCH_SCAN_OBJECTS =
|
|
592
|
+
var MAX_NUMBER_OF_BATCH_SCAN_OBJECTS = 20;
|
|
593
593
|
var MAX_CONNECTION_POOL_SIZE = 100;
|
|
594
594
|
var MAX_NUMBER_OF_RETRIES = 5;
|
|
595
595
|
var HTTP_FORCE_RETRY_STATUS_CODES = [500, 502, 503, 504];
|
|
596
|
-
var SDK_VERSION = "0.13.
|
|
596
|
+
var SDK_VERSION = "0.13.2";
|
|
597
597
|
var USER_AGENT = `PAN-AIRS/${SDK_VERSION}-typescript-sdk`;
|
|
598
598
|
var DEFAULT_MGMT_ENDPOINT = "https://api.sase.paloaltonetworks.com/aisec";
|
|
599
599
|
var DEFAULT_TOKEN_ENDPOINT = "https://auth.apps.paloaltonetworks.com/oauth2/access_token";
|
|
@@ -693,11 +693,15 @@ var AISecSDKException = class _AISecSDKException extends Error {
|
|
|
693
693
|
/**
|
|
694
694
|
* @param message - Human-readable error description.
|
|
695
695
|
* @param errorType - Classification of the error.
|
|
696
|
+
* @param metadata - Optional transport failure metadata.
|
|
696
697
|
*/
|
|
697
|
-
constructor(message, errorType) {
|
|
698
|
+
constructor(message, errorType, metadata = {}) {
|
|
698
699
|
super(errorType ? `${errorType}:${message}` : message);
|
|
699
700
|
this.name = "AISecSDKException";
|
|
700
701
|
this.errorType = errorType;
|
|
702
|
+
if (metadata.failureKind !== void 0) this.failureKind = metadata.failureKind;
|
|
703
|
+
if (metadata.statusCode !== void 0) this.statusCode = metadata.statusCode;
|
|
704
|
+
if (metadata.retryAfterMs !== void 0) this.retryAfterMs = metadata.retryAfterMs;
|
|
701
705
|
if (Error.captureStackTrace) {
|
|
702
706
|
Error.captureStackTrace(this, _AISecSDKException);
|
|
703
707
|
}
|
|
@@ -839,6 +843,59 @@ function extractErrorMessage(body, status) {
|
|
|
839
843
|
return body ? `API error ${status}: ${body}` : `API error ${status}`;
|
|
840
844
|
}
|
|
841
845
|
}
|
|
846
|
+
function parseRetryAfterHeader(value) {
|
|
847
|
+
if (value === null) return void 0;
|
|
848
|
+
const normalized = value.trim();
|
|
849
|
+
if (/^\d+$/.test(normalized)) {
|
|
850
|
+
const milliseconds = Number(normalized) * 1e3;
|
|
851
|
+
return Number.isFinite(milliseconds) ? milliseconds : void 0;
|
|
852
|
+
}
|
|
853
|
+
const weekday = "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)";
|
|
854
|
+
const longWeekday = "(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)";
|
|
855
|
+
const month = "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
|
|
856
|
+
const httpDatePatterns = [
|
|
857
|
+
new RegExp(`^${weekday}, \\d{2} ${month} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$`),
|
|
858
|
+
new RegExp(`^${longWeekday}, \\d{2}-${month}-\\d{2} \\d{2}:\\d{2}:\\d{2} GMT$`),
|
|
859
|
+
new RegExp(`^${weekday} ${month} [ \\d]\\d \\d{2}:\\d{2}:\\d{2} \\d{4}$`)
|
|
860
|
+
];
|
|
861
|
+
if (!httpDatePatterns.some((pattern) => pattern.test(normalized))) return void 0;
|
|
862
|
+
const timestamp = Date.parse(normalized);
|
|
863
|
+
if (!Number.isFinite(timestamp)) return void 0;
|
|
864
|
+
return Math.max(0, timestamp - Date.now());
|
|
865
|
+
}
|
|
866
|
+
function parseRetryAfterBody(body) {
|
|
867
|
+
try {
|
|
868
|
+
const parsed = JSON.parse(body);
|
|
869
|
+
const interval = parsed.retry_after?.interval;
|
|
870
|
+
const unit = parsed.retry_after?.unit;
|
|
871
|
+
if (typeof interval !== "number" || !Number.isFinite(interval) || interval < 0)
|
|
872
|
+
return void 0;
|
|
873
|
+
if (typeof unit !== "string") return void 0;
|
|
874
|
+
const unitMultipliers = {
|
|
875
|
+
ms: 1,
|
|
876
|
+
msec: 1,
|
|
877
|
+
msecs: 1,
|
|
878
|
+
millisecond: 1,
|
|
879
|
+
milliseconds: 1,
|
|
880
|
+
s: 1e3,
|
|
881
|
+
sec: 1e3,
|
|
882
|
+
secs: 1e3,
|
|
883
|
+
second: 1e3,
|
|
884
|
+
seconds: 1e3,
|
|
885
|
+
m: 6e4,
|
|
886
|
+
min: 6e4,
|
|
887
|
+
mins: 6e4,
|
|
888
|
+
minute: 6e4,
|
|
889
|
+
minutes: 6e4
|
|
890
|
+
};
|
|
891
|
+
const multiplier = unitMultipliers[unit.toLowerCase()];
|
|
892
|
+
if (multiplier === void 0) return void 0;
|
|
893
|
+
const milliseconds = interval * multiplier;
|
|
894
|
+
return Number.isFinite(milliseconds) ? milliseconds : void 0;
|
|
895
|
+
} catch {
|
|
896
|
+
return void 0;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
842
899
|
async function executeWithRetry(opts) {
|
|
843
900
|
const { maxRetries, execute, onRetryableFailure } = opts;
|
|
844
901
|
let lastError;
|
|
@@ -855,7 +912,8 @@ async function executeWithRetry(opts) {
|
|
|
855
912
|
}
|
|
856
913
|
throw new AISecSDKException(
|
|
857
914
|
lastError.message ?? "Network error",
|
|
858
|
-
"AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR
|
|
915
|
+
"AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */,
|
|
916
|
+
{ failureKind: "network" }
|
|
859
917
|
);
|
|
860
918
|
}
|
|
861
919
|
if (response.ok) return response;
|
|
@@ -872,11 +930,18 @@ async function executeWithRetry(opts) {
|
|
|
872
930
|
}
|
|
873
931
|
const errorText = await response.text();
|
|
874
932
|
const errorMessage = extractErrorMessage(errorText, response.status);
|
|
875
|
-
|
|
933
|
+
const retryAfterHeader = response.headers?.get?.("Retry-After") ?? null;
|
|
934
|
+
const headerRetryAfterMs = parseRetryAfterHeader(retryAfterHeader);
|
|
935
|
+
throw new AISecSDKException(errorMessage, classifyErrorType(response.status), {
|
|
936
|
+
failureKind: "http",
|
|
937
|
+
statusCode: response.status,
|
|
938
|
+
retryAfterMs: headerRetryAfterMs ?? parseRetryAfterBody(errorText)
|
|
939
|
+
});
|
|
876
940
|
}
|
|
877
941
|
throw new AISecSDKException(
|
|
878
942
|
lastError?.message ?? "Max retries exceeded",
|
|
879
|
-
"AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR
|
|
943
|
+
"AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */,
|
|
944
|
+
lastError ? { failureKind: "network" } : {}
|
|
880
945
|
);
|
|
881
946
|
}
|
|
882
947
|
|
|
@@ -1009,6 +1074,7 @@ var PromptDetectedSchema = import_zod.z.object({
|
|
|
1009
1074
|
injection: import_zod.z.boolean().optional(),
|
|
1010
1075
|
toxic_content: import_zod.z.boolean().optional(),
|
|
1011
1076
|
malicious_code: import_zod.z.boolean().optional(),
|
|
1077
|
+
source_code: import_zod.z.boolean().optional(),
|
|
1012
1078
|
agent: import_zod.z.boolean().optional(),
|
|
1013
1079
|
topic_violation: import_zod.z.boolean().optional()
|
|
1014
1080
|
}).passthrough();
|
|
@@ -1313,6 +1379,16 @@ var ThreatScanReportSchema = import_zod14.z.object({
|
|
|
1313
1379
|
}).passthrough();
|
|
1314
1380
|
|
|
1315
1381
|
// src/scan/scanner.ts
|
|
1382
|
+
function resolveNumRetries(opts) {
|
|
1383
|
+
const numRetries = opts.numRetries ?? globalConfiguration.numRetries;
|
|
1384
|
+
if (!Number.isFinite(numRetries) || !Number.isInteger(numRetries) || numRetries < 0 || numRetries > MAX_NUMBER_OF_RETRIES) {
|
|
1385
|
+
throw new AISecSDKException(
|
|
1386
|
+
`numRetries must be a finite integer between 0 and ${MAX_NUMBER_OF_RETRIES}`,
|
|
1387
|
+
"AISEC_USER_REQUEST_PAYLOAD_ERROR" /* USER_REQUEST_PAYLOAD_ERROR */
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
return numRetries;
|
|
1391
|
+
}
|
|
1316
1392
|
var Scanner = class {
|
|
1317
1393
|
/**
|
|
1318
1394
|
* @internal
|
|
@@ -1333,7 +1409,7 @@ var Scanner = class {
|
|
|
1333
1409
|
* Perform a synchronous content scan.
|
|
1334
1410
|
* @param aiProfile - AI security profile to scan against.
|
|
1335
1411
|
* @param content - Content to scan.
|
|
1336
|
-
* @param opts - Optional transaction/session IDs and
|
|
1412
|
+
* @param opts - Optional transaction/session IDs, metadata, and per-call retry override.
|
|
1337
1413
|
* @returns Scan response with verdict, action, and detection details.
|
|
1338
1414
|
* @example
|
|
1339
1415
|
* ```ts
|
|
@@ -1352,6 +1428,7 @@ var Scanner = class {
|
|
|
1352
1428
|
* ```
|
|
1353
1429
|
*/
|
|
1354
1430
|
async syncScan(aiProfile, content, opts = {}) {
|
|
1431
|
+
const numRetries = resolveNumRetries(opts);
|
|
1355
1432
|
if (opts.trId && opts.trId.length > MAX_TRANSACTION_ID_STR_LENGTH) {
|
|
1356
1433
|
throw new AISecSDKException(
|
|
1357
1434
|
`trId exceeds max length of ${MAX_TRANSACTION_ID_STR_LENGTH}`,
|
|
@@ -1378,33 +1455,52 @@ var Scanner = class {
|
|
|
1378
1455
|
body,
|
|
1379
1456
|
responseSchema: ScanResponseSchema,
|
|
1380
1457
|
auth: this.buildAuth(),
|
|
1381
|
-
numRetries
|
|
1458
|
+
numRetries
|
|
1382
1459
|
});
|
|
1383
1460
|
}
|
|
1384
1461
|
/**
|
|
1385
|
-
* Submit content for asynchronous scanning.
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1462
|
+
* Submit one batch of content for asynchronous scanning.
|
|
1463
|
+
*
|
|
1464
|
+
* The call accepts 1–20 request objects and returns one batch receipt. A batch `scan_id` can fan
|
|
1465
|
+
* out to several unordered result rows. Correlate each row by `(scan_id, req_id)`, never array
|
|
1466
|
+
* position or `scan_id` alone. The SDK preserves the server's row order and cardinality.
|
|
1467
|
+
*
|
|
1468
|
+
* Setting `numRetries: 0` guarantees one SDK fetch attempt, but cannot guarantee exactly-once
|
|
1469
|
+
* server submission after an ambiguous network or 5xx failure.
|
|
1470
|
+
* @param scanObjects - Array of scan objects (1–20 items), each with its own `req_id`.
|
|
1471
|
+
* @param opts - Optional per-call retry override.
|
|
1472
|
+
* @returns One batch receipt containing the shared scan ID for later querying.
|
|
1388
1473
|
* @example
|
|
1389
1474
|
* ```ts
|
|
1390
1475
|
* import { init, Scanner } from '@cdot65/prisma-airs-sdk';
|
|
1391
1476
|
* init();
|
|
1392
1477
|
* const scanner = new Scanner();
|
|
1393
1478
|
*
|
|
1394
|
-
* const
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1479
|
+
* const receipt = await scanner.asyncScan(
|
|
1480
|
+
* [
|
|
1481
|
+
* {
|
|
1482
|
+
* req_id: 1,
|
|
1483
|
+
* scan_req: {
|
|
1484
|
+
* ai_profile: { profile_name: 'my-profile' },
|
|
1485
|
+
* contents: [{ prompt: 'Tell me about machine learning.' }],
|
|
1486
|
+
* },
|
|
1400
1487
|
* },
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1488
|
+
* {
|
|
1489
|
+
* req_id: 2,
|
|
1490
|
+
* scan_req: {
|
|
1491
|
+
* ai_profile: { profile_name: 'my-profile' },
|
|
1492
|
+
* contents: [{ prompt: 'What are neural networks?' }],
|
|
1493
|
+
* },
|
|
1494
|
+
* },
|
|
1495
|
+
* ],
|
|
1496
|
+
* { numRetries: 0 },
|
|
1497
|
+
* );
|
|
1498
|
+
* // receipt =>
|
|
1404
1499
|
* // { received: '2024-01-01T00:00:00Z', scan_id: '550e...' }
|
|
1405
1500
|
* ```
|
|
1406
1501
|
*/
|
|
1407
|
-
async asyncScan(scanObjects) {
|
|
1502
|
+
async asyncScan(scanObjects, opts = {}) {
|
|
1503
|
+
const numRetries = resolveNumRetries(opts);
|
|
1408
1504
|
if (scanObjects.length < 1) {
|
|
1409
1505
|
throw new AISecSDKException(
|
|
1410
1506
|
"At least 1 scan object is required",
|
|
@@ -1424,13 +1520,17 @@ var Scanner = class {
|
|
|
1424
1520
|
body: scanObjects,
|
|
1425
1521
|
responseSchema: AsyncScanResponseSchema,
|
|
1426
1522
|
auth: this.buildAuth(),
|
|
1427
|
-
numRetries
|
|
1523
|
+
numRetries
|
|
1428
1524
|
});
|
|
1429
1525
|
}
|
|
1430
1526
|
/**
|
|
1431
1527
|
* Query scan results by scan IDs.
|
|
1528
|
+
*
|
|
1529
|
+
* One scan ID can return several unordered rows. Correlate them using `(scan_id, req_id)`. The
|
|
1530
|
+
* SDK does not sort, deduplicate, or collapse the response.
|
|
1432
1531
|
* @param scanIds - Array of scan UUIDs (1–5 items).
|
|
1433
|
-
* @
|
|
1532
|
+
* @param opts - Optional per-call retry override.
|
|
1533
|
+
* @returns Every scan-result row in server order, with status and response data.
|
|
1434
1534
|
* @example
|
|
1435
1535
|
* ```ts
|
|
1436
1536
|
* import { init, Scanner } from '@cdot65/prisma-airs-sdk';
|
|
@@ -1440,12 +1540,14 @@ var Scanner = class {
|
|
|
1440
1540
|
* const results = await scanner.queryByScanIds([
|
|
1441
1541
|
* '550e8400-e29b-41d4-a716-446655440000',
|
|
1442
1542
|
* ]);
|
|
1443
|
-
* //
|
|
1444
|
-
*
|
|
1445
|
-
*
|
|
1543
|
+
* // A single scan ID may produce multiple rows, for example req_id 2 then req_id 1.
|
|
1544
|
+
* for (const row of results) {
|
|
1545
|
+
* console.log(`${row.scan_id}:${row.req_id}`, row.status, row.result?.action);
|
|
1546
|
+
* }
|
|
1446
1547
|
* ```
|
|
1447
1548
|
*/
|
|
1448
|
-
async queryByScanIds(scanIds) {
|
|
1549
|
+
async queryByScanIds(scanIds, opts = {}) {
|
|
1550
|
+
const numRetries = resolveNumRetries(opts);
|
|
1449
1551
|
if (scanIds.length < 1) {
|
|
1450
1552
|
throw new AISecSDKException(
|
|
1451
1553
|
"At least 1 scan_id is required",
|
|
@@ -1470,13 +1572,17 @@ var Scanner = class {
|
|
|
1470
1572
|
params: { scan_ids: scanIds.join(",") },
|
|
1471
1573
|
responseSchema: import_zod15.z.array(ScanIdResultSchema),
|
|
1472
1574
|
auth: this.buildAuth(),
|
|
1473
|
-
numRetries
|
|
1575
|
+
numRetries
|
|
1474
1576
|
});
|
|
1475
1577
|
}
|
|
1476
1578
|
/**
|
|
1477
1579
|
* Query detailed threat reports by report IDs.
|
|
1580
|
+
*
|
|
1581
|
+
* One report ID can return several rows. Correlate them using `(report_id, req_id)`. The SDK
|
|
1582
|
+
* preserves server order and cardinality without sorting or deduplicating.
|
|
1478
1583
|
* @param reportIds - Array of report IDs (1–5 items).
|
|
1479
|
-
* @
|
|
1584
|
+
* @param opts - Optional per-call retry override.
|
|
1585
|
+
* @returns Every report row in server order, with detection details.
|
|
1480
1586
|
* @example
|
|
1481
1587
|
* ```ts
|
|
1482
1588
|
* import { init, Scanner } from '@cdot65/prisma-airs-sdk';
|
|
@@ -1484,12 +1590,13 @@ var Scanner = class {
|
|
|
1484
1590
|
* const scanner = new Scanner();
|
|
1485
1591
|
*
|
|
1486
1592
|
* const reports = await scanner.queryByReportIds(['R000...']);
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1489
|
-
*
|
|
1593
|
+
* for (const report of reports) {
|
|
1594
|
+
* console.log(`${report.report_id}:${report.req_id}`, report.detection_results);
|
|
1595
|
+
* }
|
|
1490
1596
|
* ```
|
|
1491
1597
|
*/
|
|
1492
|
-
async queryByReportIds(reportIds) {
|
|
1598
|
+
async queryByReportIds(reportIds, opts = {}) {
|
|
1599
|
+
const numRetries = resolveNumRetries(opts);
|
|
1493
1600
|
if (reportIds.length < 1) {
|
|
1494
1601
|
throw new AISecSDKException(
|
|
1495
1602
|
"At least 1 report_id is required",
|
|
@@ -1509,7 +1616,7 @@ var Scanner = class {
|
|
|
1509
1616
|
params: { report_ids: reportIds.join(",") },
|
|
1510
1617
|
responseSchema: import_zod15.z.array(ThreatScanReportSchema),
|
|
1511
1618
|
auth: this.buildAuth(),
|
|
1512
|
-
numRetries
|
|
1619
|
+
numRetries
|
|
1513
1620
|
});
|
|
1514
1621
|
}
|
|
1515
1622
|
};
|