@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 +77 -112
- package/dist/index.cjs +50 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +49 -4
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
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
|
|
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
|
-
|
|
45
|
+
### Management — Configuration CRUD (OAuth2)
|
|
46
|
+
|
|
47
|
+
CRUD operations for all three Prisma AIRS services use OAuth2:
|
|
34
48
|
|
|
35
49
|
```ts
|
|
36
|
-
import {
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
## Scanner Methods
|
|
71
|
+
### Model Security — ML Model Scanning (OAuth2)
|
|
49
72
|
|
|
50
|
-
|
|
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
|
-
|
|
76
|
+
const client = new ModelSecurityClient(); // falls back to PANW_MGMT_* env vars
|
|
58
77
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
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
|
-
###
|
|
83
|
+
### AI Red Teaming — Automated Testing (OAuth2)
|
|
72
84
|
|
|
73
85
|
```ts
|
|
74
|
-
|
|
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
|
-
|
|
88
|
+
const client = new RedTeamClient(); // falls back to PANW_MGMT_* env vars
|
|
86
89
|
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
const
|
|
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
|
-
##
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import { Content } from '@cdot65/prisma-airs-sdk';
|
|
95
|
+
## Authentication
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
110
|
-
|
|
102
|
+
```bash
|
|
103
|
+
# AI Runtime Security scans
|
|
104
|
+
export PANW_AI_SEC_API_KEY=your-api-key
|
|
111
105
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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);
|
|
127
|
-
console.error(err.errorType);
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
|
183
|
-
npm run test
|
|
184
|
-
npm run lint
|
|
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.
|
|
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
|
|
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 -
|
|
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,
|