@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.js CHANGED
@@ -23,7 +23,7 @@ var MAX_NUMBER_OF_BATCH_SCAN_OBJECTS = 5;
23
23
  var MAX_CONNECTION_POOL_SIZE = 100;
24
24
  var MAX_NUMBER_OF_RETRIES = 5;
25
25
  var HTTP_FORCE_RETRY_STATUS_CODES = [500, 502, 503, 504];
26
- var SDK_VERSION = "0.1.2";
26
+ var SDK_VERSION = "0.2.1";
27
27
  var USER_AGENT = `PAN-AIRS/${SDK_VERSION}-typescript-sdk`;
28
28
  var DEFAULT_MGMT_ENDPOINT = "https://api.sase.paloaltonetworks.com/aisec";
29
29
  var DEFAULT_TOKEN_ENDPOINT = "https://auth.apps.paloaltonetworks.com/oauth2/access_token";
@@ -54,6 +54,10 @@ var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
54
54
  })(ErrorType || {});
55
55
  var AISecSDKException = class _AISecSDKException extends Error {
56
56
  errorType;
57
+ /**
58
+ * @param message - Human-readable error description.
59
+ * @param errorType - Classification of the error.
60
+ */
57
61
  constructor(message, errorType) {
58
62
  super(errorType ? `${errorType}:${message}` : message);
59
63
  this.name = "AISecSDKException";
@@ -135,6 +139,68 @@ function init(opts = {}) {
135
139
  globalConfiguration.init(opts);
136
140
  }
137
141
 
142
+ // src/http-retry.ts
143
+ function sleep(ms) {
144
+ return new Promise((resolve) => setTimeout(resolve, ms));
145
+ }
146
+ function backoffDelay(attempt) {
147
+ return Math.pow(2, attempt) * 1e3;
148
+ }
149
+ function isRetryableStatus(status) {
150
+ return HTTP_FORCE_RETRY_STATUS_CODES.includes(status);
151
+ }
152
+ function classifyErrorType(status) {
153
+ return status >= 500 ? "AISEC_SERVER_SIDE_ERROR" /* SERVER_SIDE_ERROR */ : "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */;
154
+ }
155
+ function extractErrorMessage(body, status) {
156
+ try {
157
+ const parsed = JSON.parse(body);
158
+ return parsed.error_message ?? parsed.message ?? parsed.error?.message ?? `API error ${status}`;
159
+ } catch {
160
+ return body ? `API error ${status}: ${body}` : `API error ${status}`;
161
+ }
162
+ }
163
+ async function executeWithRetry(opts) {
164
+ const { maxRetries, execute, onRetryableFailure } = opts;
165
+ let lastError;
166
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
167
+ let response;
168
+ try {
169
+ response = await execute(attempt);
170
+ } catch (err) {
171
+ if (err instanceof AISecSDKException) throw err;
172
+ lastError = err;
173
+ if (attempt < maxRetries) {
174
+ await sleep(backoffDelay(attempt));
175
+ continue;
176
+ }
177
+ throw new AISecSDKException(
178
+ lastError.message ?? "Network error",
179
+ "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */
180
+ );
181
+ }
182
+ if (response.ok) return response;
183
+ if (onRetryableFailure) {
184
+ const handled = await onRetryableFailure(response, attempt);
185
+ if (handled) {
186
+ attempt--;
187
+ continue;
188
+ }
189
+ }
190
+ if (isRetryableStatus(response.status) && attempt < maxRetries) {
191
+ await sleep(backoffDelay(attempt));
192
+ continue;
193
+ }
194
+ const errorText = await response.text();
195
+ const errorMessage = extractErrorMessage(errorText, response.status);
196
+ throw new AISecSDKException(errorMessage, classifyErrorType(response.status));
197
+ }
198
+ throw new AISecSDKException(
199
+ lastError?.message ?? "Max retries exceeded",
200
+ "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */
201
+ );
202
+ }
203
+
138
204
  // src/utils.ts
139
205
  import { createHmac } from "crypto";
140
206
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -160,9 +226,6 @@ function buildHeaders() {
160
226
  }
161
227
  return headers;
162
228
  }
163
- function sleep(ms) {
164
- return new Promise((resolve) => setTimeout(resolve, ms));
165
- }
166
229
  async function httpRequest(opts) {
167
230
  if (!globalConfiguration.initialized) {
168
231
  throw new AISecSDKException(
@@ -185,48 +248,27 @@ async function httpRequest(opts) {
185
248
  headers[PAYLOAD_HASH] = generatePayloadHash(bodyStr, globalConfiguration.apiKey);
186
249
  }
187
250
  }
188
- const maxRetries = globalConfiguration.numRetries;
189
- let lastError;
190
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
191
- try {
192
- const response = await fetch(url.toString(), {
193
- method: opts.method,
194
- headers,
195
- body: bodyStr
196
- });
197
- if (response.ok) {
198
- const data = await response.json();
199
- return { status: response.status, data };
200
- }
201
- if (HTTP_FORCE_RETRY_STATUS_CODES.includes(response.status) && attempt < maxRetries) {
202
- await sleep(Math.pow(2, attempt) * 1e3);
203
- continue;
204
- }
205
- let errorMessage;
206
- try {
207
- const errorBody = await response.json();
208
- errorMessage = errorBody.message ?? errorBody.error?.message ?? `API error ${response.status}`;
209
- } catch {
210
- errorMessage = `API error ${response.status}`;
211
- }
212
- const errorType = response.status >= 500 ? "AISEC_SERVER_SIDE_ERROR" /* SERVER_SIDE_ERROR */ : "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */;
213
- throw new AISecSDKException(errorMessage, errorType);
214
- } catch (err) {
215
- if (err instanceof AISecSDKException) {
216
- throw err;
217
- }
218
- lastError = err;
219
- if (attempt < maxRetries) {
220
- await sleep(Math.pow(2, attempt) * 1e3);
221
- continue;
222
- }
223
- }
224
- }
225
- throw new AISecSDKException(lastError?.message ?? "Network error", "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */);
251
+ const response = await executeWithRetry({
252
+ maxRetries: globalConfiguration.numRetries,
253
+ execute: () => fetch(url.toString(), {
254
+ method: opts.method,
255
+ headers,
256
+ body: bodyStr
257
+ })
258
+ });
259
+ const data = await response.json();
260
+ return { status: response.status, data };
226
261
  }
227
262
 
228
263
  // src/scan/scanner.ts
229
264
  var Scanner = class {
265
+ /**
266
+ * Perform a synchronous content scan.
267
+ * @param aiProfile - AI security profile to scan against.
268
+ * @param content - Content to scan.
269
+ * @param opts - Optional transaction/session IDs and metadata.
270
+ * @returns Scan response with verdict, action, and detection details.
271
+ */
230
272
  async syncScan(aiProfile, content, opts = {}) {
231
273
  if (opts.trId && opts.trId.length > MAX_TRANSACTION_ID_STR_LENGTH) {
232
274
  throw new AISecSDKException(
@@ -254,6 +296,11 @@ var Scanner = class {
254
296
  });
255
297
  return res.data;
256
298
  }
299
+ /**
300
+ * Submit content for asynchronous scanning.
301
+ * @param scanObjects - Array of scan objects (1–5 items).
302
+ * @returns Response containing scan IDs for later querying.
303
+ */
257
304
  async asyncScan(scanObjects) {
258
305
  if (scanObjects.length < 1) {
259
306
  throw new AISecSDKException(
@@ -274,6 +321,11 @@ var Scanner = class {
274
321
  });
275
322
  return res.data;
276
323
  }
324
+ /**
325
+ * Query scan results by scan IDs.
326
+ * @param scanIds - Array of scan UUIDs (1–5 items).
327
+ * @returns Array of scan results with status and response data.
328
+ */
277
329
  async queryByScanIds(scanIds) {
278
330
  if (scanIds.length < 1) {
279
331
  throw new AISecSDKException(
@@ -299,6 +351,11 @@ var Scanner = class {
299
351
  });
300
352
  return res.data;
301
353
  }
354
+ /**
355
+ * Query detailed threat reports by report IDs.
356
+ * @param reportIds - Array of report IDs (1–5 items).
357
+ * @returns Array of threat scan reports with detection details.
358
+ */
302
359
  async queryByReportIds(reportIds) {
303
360
  if (reportIds.length < 1) {
304
361
  throw new AISecSDKException(
@@ -410,6 +467,7 @@ var Content = class _Content {
410
467
  set toolEvent(value) {
411
468
  this._toolEvent = value;
412
469
  }
470
+ /** Total byte length of all text content fields. */
413
471
  get length() {
414
472
  let total = 0;
415
473
  if (this._prompt) total += Buffer.byteLength(this._prompt);
@@ -419,6 +477,7 @@ var Content = class _Content {
419
477
  if (this._codeResponse) total += Buffer.byteLength(this._codeResponse);
420
478
  return total;
421
479
  }
480
+ /** Serialize to the API request format. */
422
481
  toJSON() {
423
482
  const obj = {};
424
483
  if (this._prompt !== void 0) obj.prompt = this._prompt;
@@ -429,6 +488,10 @@ var Content = class _Content {
429
488
  if (this._toolEvent !== void 0) obj.tool_event = this._toolEvent;
430
489
  return obj;
431
490
  }
491
+ /**
492
+ * Create a Content instance from an API response object.
493
+ * @param json - Scan request contents inner object.
494
+ */
432
495
  static fromJSON(json) {
433
496
  return new _Content({
434
497
  prompt: json.prompt,
@@ -439,6 +502,10 @@ var Content = class _Content {
439
502
  toolEvent: json.tool_event
440
503
  });
441
504
  }
505
+ /**
506
+ * Load content from a JSON file.
507
+ * @param filePath - Path to JSON file containing scan request contents.
508
+ */
442
509
  static fromJSONFile(filePath) {
443
510
  const raw = readFileSync(filePath, "utf-8");
444
511
  const parsed = JSON.parse(raw);
@@ -446,6 +513,23 @@ var Content = class _Content {
446
513
  }
447
514
  };
448
515
 
516
+ // src/models/enums.ts
517
+ var Verdict = {
518
+ BENIGN: "benign",
519
+ MALICIOUS: "malicious",
520
+ UNKNOWN: "unknown"
521
+ };
522
+ var Action = {
523
+ ALLOW: "allow",
524
+ BLOCK: "block",
525
+ ALERT: "alert"
526
+ };
527
+ var Category = {
528
+ BENIGN: "benign",
529
+ MALICIOUS: "malicious",
530
+ UNKNOWN: "unknown"
531
+ };
532
+
449
533
  // src/models/ai-profile.ts
450
534
  import { z } from "zod";
451
535
  var AiProfileSchema = z.object({
@@ -816,6 +900,10 @@ var OAuthClient = class {
816
900
  this.tsgId = opts.tsgId;
817
901
  this.tokenEndpoint = opts.tokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT;
818
902
  }
903
+ /**
904
+ * Get a valid access token, refreshing if needed.
905
+ * @returns Bearer access token string.
906
+ */
819
907
  async getToken() {
820
908
  if (this.accessToken && Date.now() < this.expiresAt - TOKEN_BUFFER_MS) {
821
909
  return this.accessToken;
@@ -828,6 +916,7 @@ var OAuthClient = class {
828
916
  });
829
917
  return this.pendingFetch;
830
918
  }
919
+ /** Clear the cached token, forcing a fresh fetch on next call. */
831
920
  clearToken() {
832
921
  this.accessToken = null;
833
922
  this.expiresAt = 0;
@@ -872,76 +961,43 @@ var OAuthClient = class {
872
961
  };
873
962
 
874
963
  // src/management/management-http-client.ts
875
- function sleep2(ms) {
876
- return new Promise((resolve) => setTimeout(resolve, ms));
877
- }
878
- function extractError(body, status) {
879
- try {
880
- const parsed = JSON.parse(body);
881
- return parsed.error_message ?? parsed.message ?? `API error ${status}: ${body}`;
882
- } catch {
883
- return body ? `API error ${status}: ${body}` : `API error ${status}`;
884
- }
885
- }
886
964
  async function managementHttpRequest(opts) {
887
965
  const { method, baseUrl, path, body, params, oauthClient, numRetries } = opts;
888
966
  let hadTokenRefresh = false;
889
- for (let attempt = 0; attempt <= numRetries; attempt++) {
890
- const token = await oauthClient.getToken();
891
- const stripped = baseUrl.replace(/\/+$/, "");
892
- const url = new URL(`${stripped}${path}`);
893
- if (params) {
894
- for (const [key, value] of Object.entries(params)) {
895
- url.searchParams.set(key, value);
967
+ const response = await executeWithRetry({
968
+ maxRetries: numRetries,
969
+ execute: async () => {
970
+ const token = await oauthClient.getToken();
971
+ const stripped = baseUrl.replace(/\/+$/, "");
972
+ const url = new URL(`${stripped}${path}`);
973
+ if (params) {
974
+ for (const [key, value] of Object.entries(params)) {
975
+ url.searchParams.set(key, value);
976
+ }
896
977
  }
897
- }
898
- const headers = {
899
- Authorization: `Bearer ${token}`,
900
- "User-Agent": USER_AGENT
901
- };
902
- let bodyStr;
903
- if (body !== void 0) {
904
- headers["Content-Type"] = "application/json";
905
- bodyStr = JSON.stringify(body);
906
- }
907
- let response;
908
- try {
909
- response = await fetch(url.toString(), {
910
- method,
911
- headers,
912
- body: bodyStr
913
- });
914
- } catch (err) {
915
- if (attempt < numRetries) {
916
- await sleep2(Math.pow(2, attempt) * 1e3);
917
- continue;
978
+ const headers = {
979
+ Authorization: `Bearer ${token}`,
980
+ "User-Agent": USER_AGENT
981
+ };
982
+ let bodyStr;
983
+ if (body !== void 0) {
984
+ headers["Content-Type"] = "application/json";
985
+ bodyStr = JSON.stringify(body);
918
986
  }
919
- throw new AISecSDKException(
920
- err.message ?? "Network error",
921
- "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */
922
- );
923
- }
924
- if (response.ok) {
925
- const text = await response.text();
926
- const data = text ? JSON.parse(text) : {};
927
- return { status: response.status, data };
928
- }
929
- if (response.status === 401 && !hadTokenRefresh) {
930
- hadTokenRefresh = true;
931
- oauthClient.clearToken();
932
- attempt--;
933
- continue;
934
- }
935
- if (HTTP_FORCE_RETRY_STATUS_CODES.includes(response.status) && attempt < numRetries) {
936
- await sleep2(Math.pow(2, attempt) * 1e3);
937
- continue;
987
+ return fetch(url.toString(), { method, headers, body: bodyStr });
988
+ },
989
+ onRetryableFailure: async (response2) => {
990
+ if (response2.status === 401 && !hadTokenRefresh) {
991
+ hadTokenRefresh = true;
992
+ oauthClient.clearToken();
993
+ return true;
994
+ }
995
+ return false;
938
996
  }
939
- const errorText = await response.text();
940
- const errorMessage = extractError(errorText, response.status);
941
- const errorType = response.status >= 500 ? "AISEC_SERVER_SIDE_ERROR" /* SERVER_SIDE_ERROR */ : "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */;
942
- throw new AISecSDKException(errorMessage, errorType);
943
- }
944
- throw new AISecSDKException("Max retries exceeded", "AISEC_CLIENT_SIDE_ERROR" /* CLIENT_SIDE_ERROR */);
997
+ });
998
+ const text = await response.text();
999
+ const data = text ? JSON.parse(text) : {};
1000
+ return { status: response.status, data };
945
1001
  }
946
1002
 
947
1003
  // src/management/profiles.ts
@@ -956,6 +1012,11 @@ var ProfilesClient = class {
956
1012
  this.tsgId = opts.tsgId;
957
1013
  this.numRetries = opts.numRetries;
958
1014
  }
1015
+ /**
1016
+ * Create a new security profile.
1017
+ * @param request - Profile configuration.
1018
+ * @returns The created security profile.
1019
+ */
959
1020
  async create(request) {
960
1021
  const res = await managementHttpRequest({
961
1022
  method: "POST",
@@ -967,6 +1028,11 @@ var ProfilesClient = class {
967
1028
  });
968
1029
  return res.data;
969
1030
  }
1031
+ /**
1032
+ * List security profiles for the TSG.
1033
+ * @param opts - Pagination options.
1034
+ * @returns Paginated list of security profiles.
1035
+ */
970
1036
  async list(opts) {
971
1037
  const params = {
972
1038
  offset: String(opts?.offset ?? 0),
@@ -982,6 +1048,12 @@ var ProfilesClient = class {
982
1048
  });
983
1049
  return res.data;
984
1050
  }
1051
+ /**
1052
+ * Update an existing security profile.
1053
+ * @param profileId - UUID of the profile to update.
1054
+ * @param request - Updated profile configuration.
1055
+ * @returns The updated security profile.
1056
+ */
985
1057
  async update(profileId, request) {
986
1058
  if (!isValidUuid(profileId)) {
987
1059
  throw new AISecSDKException(
@@ -999,6 +1071,11 @@ var ProfilesClient = class {
999
1071
  });
1000
1072
  return res.data;
1001
1073
  }
1074
+ /**
1075
+ * Delete a security profile.
1076
+ * @param profileId - UUID of the profile to delete.
1077
+ * @returns Deletion confirmation message.
1078
+ */
1002
1079
  async delete(profileId) {
1003
1080
  if (!isValidUuid(profileId)) {
1004
1081
  throw new AISecSDKException(
@@ -1029,6 +1106,11 @@ var TopicsClient = class {
1029
1106
  this.tsgId = opts.tsgId;
1030
1107
  this.numRetries = opts.numRetries;
1031
1108
  }
1109
+ /**
1110
+ * Create a new custom topic.
1111
+ * @param request - Topic definition with name, description, and examples.
1112
+ * @returns The created custom topic.
1113
+ */
1032
1114
  async create(request) {
1033
1115
  const res = await managementHttpRequest({
1034
1116
  method: "POST",
@@ -1040,6 +1122,11 @@ var TopicsClient = class {
1040
1122
  });
1041
1123
  return res.data;
1042
1124
  }
1125
+ /**
1126
+ * List custom topics for the TSG.
1127
+ * @param opts - Pagination options.
1128
+ * @returns Paginated list of custom topics.
1129
+ */
1043
1130
  async list(opts) {
1044
1131
  const params = {
1045
1132
  offset: String(opts?.offset ?? 0),
@@ -1055,6 +1142,12 @@ var TopicsClient = class {
1055
1142
  });
1056
1143
  return res.data;
1057
1144
  }
1145
+ /**
1146
+ * Update an existing custom topic.
1147
+ * @param topicId - UUID of the topic to update.
1148
+ * @param request - Updated topic definition.
1149
+ * @returns The updated custom topic.
1150
+ */
1058
1151
  async update(topicId, request) {
1059
1152
  if (!isValidUuid(topicId)) {
1060
1153
  throw new AISecSDKException(
@@ -1072,6 +1165,11 @@ var TopicsClient = class {
1072
1165
  });
1073
1166
  return res.data;
1074
1167
  }
1168
+ /**
1169
+ * Delete a custom topic. Fails if topic is referenced by a profile.
1170
+ * @param topicId - UUID of the topic to delete.
1171
+ * @returns Deletion confirmation message.
1172
+ */
1075
1173
  async delete(topicId) {
1076
1174
  if (!isValidUuid(topicId)) {
1077
1175
  throw new AISecSDKException(
@@ -1088,6 +1186,11 @@ var TopicsClient = class {
1088
1186
  });
1089
1187
  return res.data;
1090
1188
  }
1189
+ /**
1190
+ * Force-delete a custom topic, removing it from any referencing profiles.
1191
+ * @param topicId - UUID of the topic to force-delete.
1192
+ * @returns Deletion confirmation message.
1193
+ */
1091
1194
  async forceDelete(topicId) {
1092
1195
  if (!isValidUuid(topicId)) {
1093
1196
  throw new AISecSDKException(
@@ -1164,11 +1267,13 @@ export {
1164
1267
  AI_SEC_API_KEY,
1165
1268
  AI_SEC_API_TOKEN,
1166
1269
  ASYNC_SCAN_PATH,
1270
+ Action,
1167
1271
  AgentMetaSchema,
1168
1272
  AiProfileSchema,
1169
1273
  AsyncScanObjectSchema,
1170
1274
  AsyncScanResponseSchema,
1171
1275
  BEARER,
1276
+ Category,
1172
1277
  Content,
1173
1278
  CreateCustomTopicRequestSchema,
1174
1279
  CreateSecurityProfileRequestSchema,
@@ -1245,6 +1350,7 @@ export {
1245
1350
  TopicsClient,
1246
1351
  USER_AGENT,
1247
1352
  UrlfEntrySchema,
1353
+ Verdict,
1248
1354
  globalConfiguration,
1249
1355
  init
1250
1356
  };