@cdot65/prisma-airs-sdk 0.4.0 → 0.5.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # prisma-airs-sdk
2
2
 
3
- TypeScript SDK for Palo Alto Networks **AI Runtime Security (AIRS)**. API-compatible with the official [Python `pan-aisecurity` SDK](https://pypi.org/project/pan-aisecurity/).
3
+ TypeScript SDK for Palo Alto Networks **Prisma AIRS** — covering the full lifecycle from configuration management to operational scanning across all three service domains: **AI Runtime Security**, **AI Red Teaming**, and **Model Security**.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,14 +8,26 @@ TypeScript SDK for Palo Alto Networks **AI Runtime Security (AIRS)**. API-compat
8
8
  npm install @cdot65/prisma-airs-sdk
9
9
  ```
10
10
 
11
- Requires Node.js 18+.
11
+ Requires Node.js 18+. Zero external HTTP dependencies (native `fetch` + `crypto`).
12
+
13
+ ## What's Included
14
+
15
+ | Service | Client | Auth | Capabilities |
16
+ | ----------------------- | --------------------- | ------- | ---------------------------------------------------------- |
17
+ | **AI Runtime Security** | `Scanner` | API Key | Sync/async content scanning, prompt injection detection |
18
+ | **Management** | `ManagementClient` | OAuth2 | Security profiles and custom topics CRUD |
19
+ | **Model Security** | `ModelSecurityClient` | OAuth2 | ML model scanning, security groups, rule management |
20
+ | **AI Red Teaming** | `RedTeamClient` | OAuth2 | Automated red team scans, reports, targets, custom attacks |
21
+
22
+ All OAuth2 services share credentials and handle token lifecycle automatically (caching, proactive refresh, 401/403 auto-retry).
12
23
 
13
24
  ## Quick Start
14
25
 
26
+ ### AI Runtime Security — Content Scanning (API Key)
27
+
15
28
  ```ts
16
29
  import { init, Scanner, Content } from '@cdot65/prisma-airs-sdk';
17
30
 
18
- // Initialize (mirrors Python's aisecurity.init())
19
31
  init({ apiKey: 'YOUR_API_KEY' });
20
32
 
21
33
  const scanner = new Scanner();
@@ -30,90 +42,82 @@ console.log(result.category); // "benign" | "malicious"
30
42
  console.log(result.action); // "allow" | "block"
31
43
  ```
32
44
 
33
- ## Initialization
45
+ ### Management — Configuration CRUD (OAuth2)
46
+
47
+ CRUD operations for all three Prisma AIRS services use OAuth2:
34
48
 
35
49
  ```ts
36
- import { init } from '@cdot65/prisma-airs-sdk';
50
+ import { ManagementClient } from '@cdot65/prisma-airs-sdk';
51
+
52
+ const client = new ManagementClient(); // reads PANW_MGMT_* env vars
53
+
54
+ // Security Profiles
55
+ const profiles = await client.profiles.list();
56
+ const created = await client.profiles.create({
57
+ profile_name: 'my-profile',
58
+ active: true,
59
+ policy: {
60
+ /* ... */
61
+ },
62
+ });
37
63
 
38
- init({
39
- apiKey: 'your-api-key', // or set PANW_AI_SEC_API_KEY env var
40
- apiToken: 'your-bearer-token', // or set PANW_AI_SEC_API_TOKEN env var
41
- apiEndpoint: 'https://...', // optional, defaults to production
42
- numRetries: 3, // optional, 0-5, default 5
64
+ // Custom Topics
65
+ const topic = await client.topics.create({
66
+ topic_name: 'pii-detector',
67
+ examples: ['SSN: 123-45-6789'],
43
68
  });
44
69
  ```
45
70
 
46
- At least one of `apiKey` or `apiToken` must be provided (directly or via environment variables).
47
-
48
- ## Scanner Methods
71
+ ### Model Security ML Model Scanning (OAuth2)
49
72
 
50
- | Method | Description |
51
- | ------------------------------------- | ------------------------------------------ |
52
- | `syncScan(aiProfile, content, opts?)` | Synchronous inline scan |
53
- | `asyncScan(scanObjects)` | Batch async scan (up to 5) |
54
- | `queryByScanIds(scanIds)` | Get results by scan IDs (up to 5) |
55
- | `queryByReportIds(reportIds)` | Get threat reports by report IDs (up to 5) |
73
+ ```ts
74
+ import { ModelSecurityClient } from '@cdot65/prisma-airs-sdk';
56
75
 
57
- ### Sync Scan
76
+ const client = new ModelSecurityClient(); // falls back to PANW_MGMT_* env vars
58
77
 
59
- ```ts
60
- const result = await scanner.syncScan(
61
- { profile_name: 'my-profile' },
62
- new Content({ prompt: 'user input', response: 'model output' }),
63
- {
64
- trId: 'transaction-123',
65
- sessionId: 'session-456',
66
- metadata: { app_name: 'my-app', ai_model: 'gpt-4' },
67
- },
68
- );
78
+ const scans = await client.scans.list({ limit: 10 });
79
+ const groups = await client.securityGroups.list();
80
+ const rules = await client.securityRules.list();
69
81
  ```
70
82
 
71
- ### Async Scan
83
+ ### AI Red Teaming — Automated Testing (OAuth2)
72
84
 
73
85
  ```ts
74
- const result = await scanner.asyncScan([
75
- {
76
- req_id: 1,
77
- scan_req: {
78
- ai_profile: { profile_name: 'my-profile' },
79
- contents: [{ prompt: 'hello', response: 'world' }],
80
- },
81
- },
82
- ]);
83
- ```
86
+ import { RedTeamClient } from '@cdot65/prisma-airs-sdk';
84
87
 
85
- ### Query Results
88
+ const client = new RedTeamClient(); // falls back to PANW_MGMT_* env vars
86
89
 
87
- ```ts
88
- const results = await scanner.queryByScanIds(['scan-uuid-here']);
89
- const reports = await scanner.queryByReportIds(['report-id-here']);
90
+ const scans = await client.scans.list({ limit: 5 });
91
+ const targets = await client.targets.list();
92
+ const categories = await client.scans.getCategories();
90
93
  ```
91
94
 
92
- ## Content Class
93
-
94
- ```ts
95
- import { Content } from '@cdot65/prisma-airs-sdk';
95
+ ## Authentication
96
96
 
97
- const content = new Content({
98
- prompt: 'user prompt',
99
- response: 'model response',
100
- context: 'grounding context',
101
- codePrompt: 'code input',
102
- codeResponse: 'code output',
103
- toolEvent: {
104
- metadata: { ecosystem: 'mcp', method: 'invoke', server_name: 'my-server' },
105
- input: '{"query": "test"}',
106
- },
107
- });
97
+ | Auth Method | Used By |
98
+ | ------------------------------- | ----------------------------------------------------------- |
99
+ | **API Key** (HMAC-SHA256) | AI Runtime Security scans only |
100
+ | **OAuth2** (client_credentials) | Everything else — Management CRUD, Red Team, Model Security |
108
101
 
109
- // Serialize
110
- const json = content.toJSON();
102
+ ```bash
103
+ # AI Runtime Security scans
104
+ export PANW_AI_SEC_API_KEY=your-api-key
111
105
 
112
- // Deserialize
113
- const restored = Content.fromJSON(json);
114
- const fromFile = Content.fromJSONFile('./content.json');
106
+ # OAuth2 (shared by Management, Red Team, Model Security)
107
+ export PANW_MGMT_CLIENT_ID=your-client-id
108
+ export PANW_MGMT_CLIENT_SECRET=your-client-secret
109
+ export PANW_MGMT_TSG_ID=1234567890
115
110
  ```
116
111
 
112
+ ## Scanner Methods
113
+
114
+ | Method | Description |
115
+ | ------------------------------------- | ------------------------------------------ |
116
+ | `syncScan(aiProfile, content, opts?)` | Synchronous inline scan |
117
+ | `asyncScan(scanObjects)` | Batch async scan (up to 5) |
118
+ | `queryByScanIds(scanIds)` | Get results by scan IDs (up to 5) |
119
+ | `queryByReportIds(reportIds)` | Get threat reports by report IDs (up to 5) |
120
+
117
121
  ## Error Handling
118
122
 
119
123
  ```ts
@@ -123,65 +127,26 @@ try {
123
127
  await scanner.syncScan(profile, content);
124
128
  } catch (err) {
125
129
  if (err instanceof AISecSDKException) {
126
- console.error(err.message); // includes ErrorType prefix
127
- console.error(err.errorType); // ErrorType enum value
130
+ console.error(err.message);
131
+ console.error(err.errorType);
128
132
  }
129
133
  }
130
134
  ```
131
135
 
132
136
  Error types: `SERVER_SIDE_ERROR`, `CLIENT_SIDE_ERROR`, `USER_REQUEST_PAYLOAD_ERROR`, `MISSING_VARIABLE`, `AISEC_SDK_ERROR`, `OAUTH_ERROR`.
133
137
 
134
- ## Management API
135
-
136
- Separate client for CRUD operations on Security Profiles and Custom Topics via OAuth2 client credentials. See [docs/management-api.md](docs/management-api.md) for full details.
137
-
138
- ```bash
139
- # Required env vars (or pass as constructor options)
140
- export PANW_MGMT_CLIENT_ID=your-client-id
141
- export PANW_MGMT_CLIENT_SECRET=your-client-secret
142
- export PANW_MGMT_TSG_ID=1234567890
143
- # Optional: override for EU/UK/FedRAMP
144
- # export PANW_MGMT_ENDPOINT=https://api.eu.sase.paloaltonetworks.com/aisec
145
- ```
146
-
147
- ```ts
148
- import { ManagementClient } from '@cdot65/prisma-airs-sdk';
149
-
150
- const client = new ManagementClient();
151
-
152
- // Security Profiles
153
- const profiles = await client.profiles.list();
154
- const created = await client.profiles.create({ profile_name: 'my-profile', active: true, policy: { ... } });
155
- await client.profiles.update(created.profile_id, { ... });
156
- await client.profiles.delete(created.profile_id);
157
-
158
- // Custom Topics
159
- const topics = await client.topics.list();
160
- const topic = await client.topics.create({ topic_name: 'pii-detector', examples: ['SSN: 123-45-6789'] });
161
- await client.topics.update(topic.topic_id, { ... });
162
- await client.topics.delete(topic.topic_id);
163
- await client.topics.forceDelete(topic.topic_id); // even if referenced by a profile
164
- ```
165
-
166
- ## Migration from v0.1
138
+ ## Documentation
167
139
 
168
- | v0.1 | v0.2 |
169
- | --------------------------------------- | --------------------------------------------- |
170
- | `new PrismaAirsSdkClient({ apiToken })` | `init({ apiKey }); new Scanner()` |
171
- | `client.scanSyncRequest(body)` | `scanner.syncScan(aiProfile, content, opts?)` |
172
- | `client.scanAsyncRequest(body)` | `scanner.asyncScan(scanObjects)` |
173
- | `client.getScanResultsByScanIds(ids)` | `scanner.queryByScanIds(ids)` |
174
- | `client.getThreatScanReports(ids)` | `scanner.queryByReportIds(ids)` |
175
- | `PrismaAirsApiError` | `AISecSDKException` |
176
- | `axios` dependency | Native `fetch` (zero HTTP deps) |
140
+ Full documentation at **[cdot65.github.io/prisma-airs-sdk](https://cdot65.github.io/prisma-airs-sdk/)** — includes API reference, service guides, OAuth lifecycle docs, and examples.
177
141
 
178
142
  ## Development
179
143
 
180
144
  ```bash
181
145
  npm install
182
- npm run build # tsup (CJS + ESM + .d.ts)
183
- npm run test # vitest
184
- npm run lint # eslint
146
+ npm run build # tsup (CJS + ESM + .d.ts)
147
+ npm run test # vitest (617 tests, 99%+ coverage)
148
+ npm run lint # eslint
149
+ npm run typecheck # tsc --noEmit
185
150
  ```
186
151
 
187
152
  ## License
package/dist/index.cjs CHANGED
@@ -188,6 +188,7 @@ __export(index_exports, {
188
188
  ModelSecurityRuleResponseSchema: () => ModelSecurityRuleResponseSchema,
189
189
  ModelSecurityRulesClient: () => ModelSecurityRulesClient,
190
190
  ModelSecurityScansClient: () => ModelSecurityScansClient,
191
+ OAuthClient: () => OAuthClient,
191
192
  PAYLOAD_HASH: () => PAYLOAD_HASH,
192
193
  PolicySchema: () => PolicySchema,
193
194
  PolicyType: () => PolicyType,
@@ -361,7 +362,7 @@ var MAX_NUMBER_OF_BATCH_SCAN_OBJECTS = 5;
361
362
  var MAX_CONNECTION_POOL_SIZE = 100;
362
363
  var MAX_NUMBER_OF_RETRIES = 5;
363
364
  var HTTP_FORCE_RETRY_STATUS_CODES = [500, 502, 503, 504];
364
- var SDK_VERSION = "0.4.0";
365
+ var SDK_VERSION = "0.5.1";
365
366
  var USER_AGENT = `PAN-AIRS/${SDK_VERSION}-typescript-sdk`;
366
367
  var DEFAULT_MGMT_ENDPOINT = "https://api.sase.paloaltonetworks.com/aisec";
367
368
  var DEFAULT_TOKEN_ENDPOINT = "https://auth.apps.paloaltonetworks.com/oauth2/access_token";
@@ -2647,12 +2648,14 @@ var OAuthTokenResponseSchema = import_zod19.z.object({
2647
2648
  });
2648
2649
 
2649
2650
  // src/management/oauth-client.ts
2650
- var TOKEN_BUFFER_MS = 3e4;
2651
+ var DEFAULT_TOKEN_BUFFER_MS = 3e4;
2651
2652
  var OAuthClient = class {
2652
2653
  tokenEndpoint;
2653
2654
  clientId;
2654
2655
  clientSecret;
2655
2656
  tsgId;
2657
+ tokenBufferMs;
2658
+ onTokenRefresh;
2656
2659
  accessToken = null;
2657
2660
  expiresAt = 0;
2658
2661
  pendingFetch = null;
@@ -2661,13 +2664,15 @@ var OAuthClient = class {
2661
2664
  this.clientSecret = opts.clientSecret;
2662
2665
  this.tsgId = opts.tsgId;
2663
2666
  this.tokenEndpoint = opts.tokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT;
2667
+ this.tokenBufferMs = opts.tokenBufferMs ?? DEFAULT_TOKEN_BUFFER_MS;
2668
+ this.onTokenRefresh = opts.onTokenRefresh;
2664
2669
  }
2665
2670
  /**
2666
2671
  * Get a valid access token, refreshing if needed.
2667
2672
  * @returns Bearer access token string.
2668
2673
  */
2669
2674
  async getToken() {
2670
- if (this.accessToken && Date.now() < this.expiresAt - TOKEN_BUFFER_MS) {
2675
+ if (this.accessToken && Date.now() < this.expiresAt - this.tokenBufferMs) {
2671
2676
  return this.accessToken;
2672
2677
  }
2673
2678
  if (this.pendingFetch) {
@@ -2683,6 +2688,40 @@ var OAuthClient = class {
2683
2688
  this.accessToken = null;
2684
2689
  this.expiresAt = 0;
2685
2690
  }
2691
+ /** Check if the current token has passed its expiry time. Returns true if no token exists. */
2692
+ isTokenExpired() {
2693
+ if (!this.accessToken) return true;
2694
+ return Date.now() >= this.expiresAt;
2695
+ }
2696
+ /**
2697
+ * Check if the token is within the pre-expiry buffer window.
2698
+ * Returns true if no token exists.
2699
+ * @param bufferMs - Custom buffer in ms. Defaults to the configured `tokenBufferMs`.
2700
+ */
2701
+ isTokenExpiringSoon(bufferMs) {
2702
+ if (!this.accessToken) return true;
2703
+ const buffer = bufferMs ?? this.tokenBufferMs;
2704
+ return Date.now() >= this.expiresAt - buffer;
2705
+ }
2706
+ /**
2707
+ * Get a snapshot of the current token state without exposing the actual token value.
2708
+ * @returns Current {@link TokenInfo}.
2709
+ */
2710
+ getTokenInfo() {
2711
+ const now = Date.now();
2712
+ const hasToken = this.accessToken !== null;
2713
+ const isExpired = !hasToken || now >= this.expiresAt;
2714
+ const isExpiringSoon = !hasToken || now >= this.expiresAt - this.tokenBufferMs;
2715
+ const expiresInMs = hasToken ? Math.max(0, this.expiresAt - now) : 0;
2716
+ return {
2717
+ hasToken,
2718
+ isValid: hasToken && !isExpiringSoon,
2719
+ isExpired,
2720
+ isExpiringSoon,
2721
+ expiresInMs,
2722
+ expiresAt: hasToken ? this.expiresAt : 0
2723
+ };
2724
+ }
2686
2725
  async fetchToken() {
2687
2726
  const credentials = btoa(`${this.clientId}:${this.clientSecret}`);
2688
2727
  const body = new URLSearchParams({
@@ -2718,6 +2757,12 @@ var OAuthClient = class {
2718
2757
  const data = OAuthTokenResponseSchema.parse(await response.json());
2719
2758
  this.accessToken = data.access_token;
2720
2759
  this.expiresAt = Date.now() + data.expires_in * 1e3;
2760
+ if (this.onTokenRefresh) {
2761
+ try {
2762
+ this.onTokenRefresh(this.getTokenInfo());
2763
+ } catch {
2764
+ }
2765
+ }
2721
2766
  return this.accessToken;
2722
2767
  }
2723
2768
  };
@@ -2749,7 +2794,7 @@ async function managementHttpRequest(opts) {
2749
2794
  return fetch(url.toString(), { method, headers, body: bodyStr });
2750
2795
  },
2751
2796
  onRetryableFailure: async (response2) => {
2752
- if (response2.status === 401 && !hadTokenRefresh) {
2797
+ if ((response2.status === 401 || response2.status === 403) && !hadTokenRefresh) {
2753
2798
  hadTokenRefresh = true;
2754
2799
  oauthClient.clearToken();
2755
2800
  return true;
@@ -4895,6 +4940,7 @@ var RedTeamClient = class {
4895
4940
  ModelSecurityRuleResponseSchema,
4896
4941
  ModelSecurityRulesClient,
4897
4942
  ModelSecurityScansClient,
4943
+ OAuthClient,
4898
4944
  PAYLOAD_HASH,
4899
4945
  PolicySchema,
4900
4946
  PolicyType,