@cdot65/prisma-airs-sdk 0.2.0 → 0.2.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/dist/index.cjs CHANGED
@@ -25,11 +25,13 @@ __export(index_exports, {
25
25
  AI_SEC_API_KEY: () => AI_SEC_API_KEY,
26
26
  AI_SEC_API_TOKEN: () => AI_SEC_API_TOKEN,
27
27
  ASYNC_SCAN_PATH: () => ASYNC_SCAN_PATH,
28
+ Action: () => Action,
28
29
  AgentMetaSchema: () => AgentMetaSchema,
29
30
  AiProfileSchema: () => AiProfileSchema,
30
31
  AsyncScanObjectSchema: () => AsyncScanObjectSchema,
31
32
  AsyncScanResponseSchema: () => AsyncScanResponseSchema,
32
33
  BEARER: () => BEARER,
34
+ Category: () => Category,
33
35
  Content: () => Content,
34
36
  CreateCustomTopicRequestSchema: () => CreateCustomTopicRequestSchema,
35
37
  CreateSecurityProfileRequestSchema: () => CreateSecurityProfileRequestSchema,
@@ -106,6 +108,7 @@ __export(index_exports, {
106
108
  TopicsClient: () => TopicsClient,
107
109
  USER_AGENT: () => USER_AGENT,
108
110
  UrlfEntrySchema: () => UrlfEntrySchema,
111
+ Verdict: () => Verdict,
109
112
  globalConfiguration: () => globalConfiguration,
110
113
  init: () => init
111
114
  });
@@ -136,7 +139,7 @@ var MAX_NUMBER_OF_BATCH_SCAN_OBJECTS = 5;
136
139
  var MAX_CONNECTION_POOL_SIZE = 100;
137
140
  var MAX_NUMBER_OF_RETRIES = 5;
138
141
  var HTTP_FORCE_RETRY_STATUS_CODES = [500, 502, 503, 504];
139
- var SDK_VERSION = "0.1.2";
142
+ var SDK_VERSION = "0.2.1";
140
143
  var USER_AGENT = `PAN-AIRS/${SDK_VERSION}-typescript-sdk`;
141
144
  var DEFAULT_MGMT_ENDPOINT = "https://api.sase.paloaltonetworks.com/aisec";
142
145
  var DEFAULT_TOKEN_ENDPOINT = "https://auth.apps.paloaltonetworks.com/oauth2/access_token";
@@ -167,6 +170,10 @@ var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
167
170
  })(ErrorType || {});
168
171
  var AISecSDKException = class _AISecSDKException extends Error {
169
172
  errorType;
173
+ /**
174
+ * @param message - Human-readable error description.
175
+ * @param errorType - Classification of the error.
176
+ */
170
177
  constructor(message, errorType) {
171
178
  super(errorType ? `${errorType}:${message}` : message);
172
179
  this.name = "AISecSDKException";
@@ -248,6 +255,68 @@ function init(opts = {}) {
248
255
  globalConfiguration.init(opts);
249
256
  }
250
257
 
258
+ // src/http-retry.ts
259
+ function sleep(ms) {
260
+ return new Promise((resolve) => setTimeout(resolve, ms));
261
+ }
262
+ function backoffDelay(attempt) {
263
+ return Math.pow(2, attempt) * 1e3;
264
+ }
265
+ function isRetryableStatus(status) {
266
+ return HTTP_FORCE_RETRY_STATUS_CODES.includes(status);
267
+ }
268
+ function classifyErrorType(status) {
269
+ return status >= 500 ? "AISEC_SERVER_SIDE_ERROR" /* SERVER_SIDE_ERROR */ : "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */;
270
+ }
271
+ function extractErrorMessage(body, status) {
272
+ try {
273
+ const parsed = JSON.parse(body);
274
+ return parsed.error_message ?? parsed.message ?? parsed.error?.message ?? `API error ${status}`;
275
+ } catch {
276
+ return body ? `API error ${status}: ${body}` : `API error ${status}`;
277
+ }
278
+ }
279
+ async function executeWithRetry(opts) {
280
+ const { maxRetries, execute, onRetryableFailure } = opts;
281
+ let lastError;
282
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
283
+ let response;
284
+ try {
285
+ response = await execute(attempt);
286
+ } catch (err) {
287
+ if (err instanceof AISecSDKException) throw err;
288
+ lastError = err;
289
+ if (attempt < maxRetries) {
290
+ await sleep(backoffDelay(attempt));
291
+ continue;
292
+ }
293
+ throw new AISecSDKException(
294
+ lastError.message ?? "Network error",
295
+ "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */
296
+ );
297
+ }
298
+ if (response.ok) return response;
299
+ if (onRetryableFailure) {
300
+ const handled = await onRetryableFailure(response, attempt);
301
+ if (handled) {
302
+ attempt--;
303
+ continue;
304
+ }
305
+ }
306
+ if (isRetryableStatus(response.status) && attempt < maxRetries) {
307
+ await sleep(backoffDelay(attempt));
308
+ continue;
309
+ }
310
+ const errorText = await response.text();
311
+ const errorMessage = extractErrorMessage(errorText, response.status);
312
+ throw new AISecSDKException(errorMessage, classifyErrorType(response.status));
313
+ }
314
+ throw new AISecSDKException(
315
+ lastError?.message ?? "Max retries exceeded",
316
+ "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */
317
+ );
318
+ }
319
+
251
320
  // src/utils.ts
252
321
  var import_node_crypto = require("crypto");
253
322
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -273,9 +342,6 @@ function buildHeaders() {
273
342
  }
274
343
  return headers;
275
344
  }
276
- function sleep(ms) {
277
- return new Promise((resolve) => setTimeout(resolve, ms));
278
- }
279
345
  async function httpRequest(opts) {
280
346
  if (!globalConfiguration.initialized) {
281
347
  throw new AISecSDKException(
@@ -298,48 +364,27 @@ async function httpRequest(opts) {
298
364
  headers[PAYLOAD_HASH] = generatePayloadHash(bodyStr, globalConfiguration.apiKey);
299
365
  }
300
366
  }
301
- const maxRetries = globalConfiguration.numRetries;
302
- let lastError;
303
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
304
- try {
305
- const response = await fetch(url.toString(), {
306
- method: opts.method,
307
- headers,
308
- body: bodyStr
309
- });
310
- if (response.ok) {
311
- const data = await response.json();
312
- return { status: response.status, data };
313
- }
314
- if (HTTP_FORCE_RETRY_STATUS_CODES.includes(response.status) && attempt < maxRetries) {
315
- await sleep(Math.pow(2, attempt) * 1e3);
316
- continue;
317
- }
318
- let errorMessage;
319
- try {
320
- const errorBody = await response.json();
321
- errorMessage = errorBody.message ?? errorBody.error?.message ?? `API error ${response.status}`;
322
- } catch {
323
- errorMessage = `API error ${response.status}`;
324
- }
325
- const errorType = response.status >= 500 ? "AISEC_SERVER_SIDE_ERROR" /* SERVER_SIDE_ERROR */ : "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */;
326
- throw new AISecSDKException(errorMessage, errorType);
327
- } catch (err) {
328
- if (err instanceof AISecSDKException) {
329
- throw err;
330
- }
331
- lastError = err;
332
- if (attempt < maxRetries) {
333
- await sleep(Math.pow(2, attempt) * 1e3);
334
- continue;
335
- }
336
- }
337
- }
338
- throw new AISecSDKException(lastError?.message ?? "Network error", "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */);
367
+ const response = await executeWithRetry({
368
+ maxRetries: globalConfiguration.numRetries,
369
+ execute: () => fetch(url.toString(), {
370
+ method: opts.method,
371
+ headers,
372
+ body: bodyStr
373
+ })
374
+ });
375
+ const data = await response.json();
376
+ return { status: response.status, data };
339
377
  }
340
378
 
341
379
  // src/scan/scanner.ts
342
380
  var Scanner = class {
381
+ /**
382
+ * Perform a synchronous content scan.
383
+ * @param aiProfile - AI security profile to scan against.
384
+ * @param content - Content to scan.
385
+ * @param opts - Optional transaction/session IDs and metadata.
386
+ * @returns Scan response with verdict, action, and detection details.
387
+ */
343
388
  async syncScan(aiProfile, content, opts = {}) {
344
389
  if (opts.trId && opts.trId.length > MAX_TRANSACTION_ID_STR_LENGTH) {
345
390
  throw new AISecSDKException(
@@ -367,6 +412,11 @@ var Scanner = class {
367
412
  });
368
413
  return res.data;
369
414
  }
415
+ /**
416
+ * Submit content for asynchronous scanning.
417
+ * @param scanObjects - Array of scan objects (1–5 items).
418
+ * @returns Response containing scan IDs for later querying.
419
+ */
370
420
  async asyncScan(scanObjects) {
371
421
  if (scanObjects.length < 1) {
372
422
  throw new AISecSDKException(
@@ -387,6 +437,11 @@ var Scanner = class {
387
437
  });
388
438
  return res.data;
389
439
  }
440
+ /**
441
+ * Query scan results by scan IDs.
442
+ * @param scanIds - Array of scan UUIDs (1–5 items).
443
+ * @returns Array of scan results with status and response data.
444
+ */
390
445
  async queryByScanIds(scanIds) {
391
446
  if (scanIds.length < 1) {
392
447
  throw new AISecSDKException(
@@ -412,6 +467,11 @@ var Scanner = class {
412
467
  });
413
468
  return res.data;
414
469
  }
470
+ /**
471
+ * Query detailed threat reports by report IDs.
472
+ * @param reportIds - Array of report IDs (1–5 items).
473
+ * @returns Array of threat scan reports with detection details.
474
+ */
415
475
  async queryByReportIds(reportIds) {
416
476
  if (reportIds.length < 1) {
417
477
  throw new AISecSDKException(
@@ -523,6 +583,7 @@ var Content = class _Content {
523
583
  set toolEvent(value) {
524
584
  this._toolEvent = value;
525
585
  }
586
+ /** Total byte length of all text content fields. */
526
587
  get length() {
527
588
  let total = 0;
528
589
  if (this._prompt) total += Buffer.byteLength(this._prompt);
@@ -532,6 +593,7 @@ var Content = class _Content {
532
593
  if (this._codeResponse) total += Buffer.byteLength(this._codeResponse);
533
594
  return total;
534
595
  }
596
+ /** Serialize to the API request format. */
535
597
  toJSON() {
536
598
  const obj = {};
537
599
  if (this._prompt !== void 0) obj.prompt = this._prompt;
@@ -542,6 +604,10 @@ var Content = class _Content {
542
604
  if (this._toolEvent !== void 0) obj.tool_event = this._toolEvent;
543
605
  return obj;
544
606
  }
607
+ /**
608
+ * Create a Content instance from an API response object.
609
+ * @param json - Scan request contents inner object.
610
+ */
545
611
  static fromJSON(json) {
546
612
  return new _Content({
547
613
  prompt: json.prompt,
@@ -552,6 +618,10 @@ var Content = class _Content {
552
618
  toolEvent: json.tool_event
553
619
  });
554
620
  }
621
+ /**
622
+ * Load content from a JSON file.
623
+ * @param filePath - Path to JSON file containing scan request contents.
624
+ */
555
625
  static fromJSONFile(filePath) {
556
626
  const raw = (0, import_node_fs.readFileSync)(filePath, "utf-8");
557
627
  const parsed = JSON.parse(raw);
@@ -559,6 +629,23 @@ var Content = class _Content {
559
629
  }
560
630
  };
561
631
 
632
+ // src/models/enums.ts
633
+ var Verdict = {
634
+ BENIGN: "benign",
635
+ MALICIOUS: "malicious",
636
+ UNKNOWN: "unknown"
637
+ };
638
+ var Action = {
639
+ ALLOW: "allow",
640
+ BLOCK: "block",
641
+ ALERT: "alert"
642
+ };
643
+ var Category = {
644
+ BENIGN: "benign",
645
+ MALICIOUS: "malicious",
646
+ UNKNOWN: "unknown"
647
+ };
648
+
562
649
  // src/models/ai-profile.ts
563
650
  var import_zod = require("zod");
564
651
  var AiProfileSchema = import_zod.z.object({
@@ -929,6 +1016,10 @@ var OAuthClient = class {
929
1016
  this.tsgId = opts.tsgId;
930
1017
  this.tokenEndpoint = opts.tokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT;
931
1018
  }
1019
+ /**
1020
+ * Get a valid access token, refreshing if needed.
1021
+ * @returns Bearer access token string.
1022
+ */
932
1023
  async getToken() {
933
1024
  if (this.accessToken && Date.now() < this.expiresAt - TOKEN_BUFFER_MS) {
934
1025
  return this.accessToken;
@@ -941,6 +1032,7 @@ var OAuthClient = class {
941
1032
  });
942
1033
  return this.pendingFetch;
943
1034
  }
1035
+ /** Clear the cached token, forcing a fresh fetch on next call. */
944
1036
  clearToken() {
945
1037
  this.accessToken = null;
946
1038
  this.expiresAt = 0;
@@ -985,76 +1077,43 @@ var OAuthClient = class {
985
1077
  };
986
1078
 
987
1079
  // src/management/management-http-client.ts
988
- function sleep2(ms) {
989
- return new Promise((resolve) => setTimeout(resolve, ms));
990
- }
991
- function extractError(body, status) {
992
- try {
993
- const parsed = JSON.parse(body);
994
- return parsed.error_message ?? parsed.message ?? `API error ${status}: ${body}`;
995
- } catch {
996
- return body ? `API error ${status}: ${body}` : `API error ${status}`;
997
- }
998
- }
999
1080
  async function managementHttpRequest(opts) {
1000
1081
  const { method, baseUrl, path, body, params, oauthClient, numRetries } = opts;
1001
1082
  let hadTokenRefresh = false;
1002
- for (let attempt = 0; attempt <= numRetries; attempt++) {
1003
- const token = await oauthClient.getToken();
1004
- const stripped = baseUrl.replace(/\/+$/, "");
1005
- const url = new URL(`${stripped}${path}`);
1006
- if (params) {
1007
- for (const [key, value] of Object.entries(params)) {
1008
- url.searchParams.set(key, value);
1083
+ const response = await executeWithRetry({
1084
+ maxRetries: numRetries,
1085
+ execute: async () => {
1086
+ const token = await oauthClient.getToken();
1087
+ const stripped = baseUrl.replace(/\/+$/, "");
1088
+ const url = new URL(`${stripped}${path}`);
1089
+ if (params) {
1090
+ for (const [key, value] of Object.entries(params)) {
1091
+ url.searchParams.set(key, value);
1092
+ }
1009
1093
  }
1010
- }
1011
- const headers = {
1012
- Authorization: `Bearer ${token}`,
1013
- "User-Agent": USER_AGENT
1014
- };
1015
- let bodyStr;
1016
- if (body !== void 0) {
1017
- headers["Content-Type"] = "application/json";
1018
- bodyStr = JSON.stringify(body);
1019
- }
1020
- let response;
1021
- try {
1022
- response = await fetch(url.toString(), {
1023
- method,
1024
- headers,
1025
- body: bodyStr
1026
- });
1027
- } catch (err) {
1028
- if (attempt < numRetries) {
1029
- await sleep2(Math.pow(2, attempt) * 1e3);
1030
- continue;
1094
+ const headers = {
1095
+ Authorization: `Bearer ${token}`,
1096
+ "User-Agent": USER_AGENT
1097
+ };
1098
+ let bodyStr;
1099
+ if (body !== void 0) {
1100
+ headers["Content-Type"] = "application/json";
1101
+ bodyStr = JSON.stringify(body);
1031
1102
  }
1032
- throw new AISecSDKException(
1033
- err.message ?? "Network error",
1034
- "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */
1035
- );
1036
- }
1037
- if (response.ok) {
1038
- const text = await response.text();
1039
- const data = text ? JSON.parse(text) : {};
1040
- return { status: response.status, data };
1041
- }
1042
- if (response.status === 401 && !hadTokenRefresh) {
1043
- hadTokenRefresh = true;
1044
- oauthClient.clearToken();
1045
- attempt--;
1046
- continue;
1047
- }
1048
- if (HTTP_FORCE_RETRY_STATUS_CODES.includes(response.status) && attempt < numRetries) {
1049
- await sleep2(Math.pow(2, attempt) * 1e3);
1050
- continue;
1103
+ return fetch(url.toString(), { method, headers, body: bodyStr });
1104
+ },
1105
+ onRetryableFailure: async (response2) => {
1106
+ if (response2.status === 401 && !hadTokenRefresh) {
1107
+ hadTokenRefresh = true;
1108
+ oauthClient.clearToken();
1109
+ return true;
1110
+ }
1111
+ return false;
1051
1112
  }
1052
- const errorText = await response.text();
1053
- const errorMessage = extractError(errorText, response.status);
1054
- const errorType = response.status >= 500 ? "AISEC_SERVER_SIDE_ERROR" /* SERVER_SIDE_ERROR */ : "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */;
1055
- throw new AISecSDKException(errorMessage, errorType);
1056
- }
1057
- throw new AISecSDKException("Max retries exceeded", "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */);
1113
+ });
1114
+ const text = await response.text();
1115
+ const data = text ? JSON.parse(text) : {};
1116
+ return { status: response.status, data };
1058
1117
  }
1059
1118
 
1060
1119
  // src/management/profiles.ts
@@ -1069,6 +1128,11 @@ var ProfilesClient = class {
1069
1128
  this.tsgId = opts.tsgId;
1070
1129
  this.numRetries = opts.numRetries;
1071
1130
  }
1131
+ /**
1132
+ * Create a new security profile.
1133
+ * @param request - Profile configuration.
1134
+ * @returns The created security profile.
1135
+ */
1072
1136
  async create(request) {
1073
1137
  const res = await managementHttpRequest({
1074
1138
  method: "POST",
@@ -1080,6 +1144,11 @@ var ProfilesClient = class {
1080
1144
  });
1081
1145
  return res.data;
1082
1146
  }
1147
+ /**
1148
+ * List security profiles for the TSG.
1149
+ * @param opts - Pagination options.
1150
+ * @returns Paginated list of security profiles.
1151
+ */
1083
1152
  async list(opts) {
1084
1153
  const params = {
1085
1154
  offset: String(opts?.offset ?? 0),
@@ -1095,6 +1164,12 @@ var ProfilesClient = class {
1095
1164
  });
1096
1165
  return res.data;
1097
1166
  }
1167
+ /**
1168
+ * Update an existing security profile.
1169
+ * @param profileId - UUID of the profile to update.
1170
+ * @param request - Updated profile configuration.
1171
+ * @returns The updated security profile.
1172
+ */
1098
1173
  async update(profileId, request) {
1099
1174
  if (!isValidUuid(profileId)) {
1100
1175
  throw new AISecSDKException(
@@ -1112,6 +1187,11 @@ var ProfilesClient = class {
1112
1187
  });
1113
1188
  return res.data;
1114
1189
  }
1190
+ /**
1191
+ * Delete a security profile.
1192
+ * @param profileId - UUID of the profile to delete.
1193
+ * @returns Deletion confirmation message.
1194
+ */
1115
1195
  async delete(profileId) {
1116
1196
  if (!isValidUuid(profileId)) {
1117
1197
  throw new AISecSDKException(
@@ -1142,6 +1222,11 @@ var TopicsClient = class {
1142
1222
  this.tsgId = opts.tsgId;
1143
1223
  this.numRetries = opts.numRetries;
1144
1224
  }
1225
+ /**
1226
+ * Create a new custom topic.
1227
+ * @param request - Topic definition with name, description, and examples.
1228
+ * @returns The created custom topic.
1229
+ */
1145
1230
  async create(request) {
1146
1231
  const res = await managementHttpRequest({
1147
1232
  method: "POST",
@@ -1153,6 +1238,11 @@ var TopicsClient = class {
1153
1238
  });
1154
1239
  return res.data;
1155
1240
  }
1241
+ /**
1242
+ * List custom topics for the TSG.
1243
+ * @param opts - Pagination options.
1244
+ * @returns Paginated list of custom topics.
1245
+ */
1156
1246
  async list(opts) {
1157
1247
  const params = {
1158
1248
  offset: String(opts?.offset ?? 0),
@@ -1168,6 +1258,12 @@ var TopicsClient = class {
1168
1258
  });
1169
1259
  return res.data;
1170
1260
  }
1261
+ /**
1262
+ * Update an existing custom topic.
1263
+ * @param topicId - UUID of the topic to update.
1264
+ * @param request - Updated topic definition.
1265
+ * @returns The updated custom topic.
1266
+ */
1171
1267
  async update(topicId, request) {
1172
1268
  if (!isValidUuid(topicId)) {
1173
1269
  throw new AISecSDKException(
@@ -1185,6 +1281,11 @@ var TopicsClient = class {
1185
1281
  });
1186
1282
  return res.data;
1187
1283
  }
1284
+ /**
1285
+ * Delete a custom topic. Fails if topic is referenced by a profile.
1286
+ * @param topicId - UUID of the topic to delete.
1287
+ * @returns Deletion confirmation message.
1288
+ */
1188
1289
  async delete(topicId) {
1189
1290
  if (!isValidUuid(topicId)) {
1190
1291
  throw new AISecSDKException(
@@ -1201,6 +1302,11 @@ var TopicsClient = class {
1201
1302
  });
1202
1303
  return res.data;
1203
1304
  }
1305
+ /**
1306
+ * Force-delete a custom topic, removing it from any referencing profiles.
1307
+ * @param topicId - UUID of the topic to force-delete.
1308
+ * @returns Deletion confirmation message.
1309
+ */
1204
1310
  async forceDelete(topicId) {
1205
1311
  if (!isValidUuid(topicId)) {
1206
1312
  throw new AISecSDKException(
@@ -1278,11 +1384,13 @@ var ManagementClient = class {
1278
1384
  AI_SEC_API_KEY,
1279
1385
  AI_SEC_API_TOKEN,
1280
1386
  ASYNC_SCAN_PATH,
1387
+ Action,
1281
1388
  AgentMetaSchema,
1282
1389
  AiProfileSchema,
1283
1390
  AsyncScanObjectSchema,
1284
1391
  AsyncScanResponseSchema,
1285
1392
  BEARER,
1393
+ Category,
1286
1394
  Content,
1287
1395
  CreateCustomTopicRequestSchema,
1288
1396
  CreateSecurityProfileRequestSchema,
@@ -1359,6 +1467,7 @@ var ManagementClient = class {
1359
1467
  TopicsClient,
1360
1468
  USER_AGENT,
1361
1469
  UrlfEntrySchema,
1470
+ Verdict,
1362
1471
  globalConfiguration,
1363
1472
  init
1364
1473
  });