Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/0186-red-team-network-broker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cdot65/prisma-airs-sdk': minor
---

Add a Red Team Network Broker channel sub-client. `RedTeamClient.networkBroker` exposes `listChannels`, `createChannel`, `getChannel`, `updateChannel`, and `getChannelStats` against the network broker data plane, letting you discover and manage the channel UUIDs referenced by Red Team targets (`network_broker_channel_uuid`). The endpoint is overridable via the `networkBrokerEndpoint` option or `PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT`. Adds typed request/response models and Zod schemas (`Channel`, `ChannelStats`, `ChannelStatus`, create/update requests, and channel list types).
1 change: 1 addition & 0 deletions docs-site/docs/getting-started/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Falls back to `PANW_MGMT_*` variables if service-specific ones are not set.
| `PANW_RED_TEAM_TSG_ID` | Falls back to MGMT | — |
| `PANW_RED_TEAM_DATA_ENDPOINT` | No | `https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane` |
| `PANW_RED_TEAM_MGMT_ENDPOINT` | No | `https://api.sase.paloaltonetworks.com/ai-red-teaming/mgmt-plane` |
| `PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT` | No | `https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane/network-broker` |
| `PANW_RED_TEAM_TOKEN_ENDPOINT` | Falls back to MGMT | — |

## Regional Endpoints
Expand Down
1 change: 1 addition & 0 deletions docs-site/docs/getting-started/environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ All fall back to the corresponding `PANW_MGMT_*` variable if not set.
| `PANW_RED_TEAM_TSG_ID` | `PANW_MGMT_TSG_ID` | — | Tenant Service Group ID |
| `PANW_RED_TEAM_DATA_ENDPOINT` | — | `https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane` | Data plane base URL |
| `PANW_RED_TEAM_MGMT_ENDPOINT` | — | `https://api.sase.paloaltonetworks.com/ai-red-teaming/mgmt-plane` | Management plane base URL |
| `PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT` | — | `https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane/network-broker` | Network broker base URL |
| `PANW_RED_TEAM_TOKEN_ENDPOINT` | `PANW_MGMT_TOKEN_ENDPOINT` | — | OAuth2 token endpoint |

## Debugging
Expand Down
57 changes: 47 additions & 10 deletions docs-site/docs/guides/red-team-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The Red Team API uses OAuth2 `client_credentials` flow. Each env var falls back
| `PANW_RED_TEAM_TSG_ID` | `PANW_MGMT_TSG_ID` | Yes | Tenant Service Group ID |
| `PANW_RED_TEAM_DATA_ENDPOINT` | -- | No | Data plane URL (default: `https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane`) |
| `PANW_RED_TEAM_MGMT_ENDPOINT` | -- | No | Mgmt plane URL (default: `https://api.sase.paloaltonetworks.com/ai-red-teaming/mgmt-plane`) |
| `PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT` | -- | No | Network broker URL (default: `https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane/network-broker`) |
| `PANW_RED_TEAM_TOKEN_ENDPOINT` | `PANW_MGMT_TOKEN_ENDPOINT` | No | Token URL (default: `https://auth.apps.paloaltonetworks.com/oauth2/access_token`) |

### Setup
Expand Down Expand Up @@ -79,17 +80,20 @@ Token fetch, caching, and refresh are handled automatically. Retries (up to 5) u

## Sub-Clients

The `RedTeamClient` exposes seven sub-clients:
The `RedTeamClient` exposes eight sub-clients:

| Sub-Client | Plane | Access |
| --------------------- | ---------- | ---------------------------- |
| `scans` | Data | `client.scans` |
| `reports` | Data | `client.reports` |
| `customAttackReports` | Data | `client.customAttackReports` |
| `targets` | Management | `client.targets` |
| `customAttacks` | Management | `client.customAttacks` |
| `eula` | Management | `client.eula` |
| `instances` | Management | `client.instances` |
| Sub-Client | Plane | Access |
| --------------------- | -------------- | ---------------------------- |
| `scans` | Data | `client.scans` |
| `reports` | Data | `client.reports` |
| `customAttackReports` | Data | `client.customAttackReports` |
| `targets` | Management | `client.targets` |
| `customAttacks` | Management | `client.customAttacks` |
| `eula` | Management | `client.eula` |
| `instances` | Management | `client.instances` |
| `networkBroker` | Network Broker | `client.networkBroker` |

`networkBroker` talks to a **distinct base URL** — the network broker data plane (`.../ai-red-teaming/data-plane/network-broker`) — while sharing the same Red Team OAuth credentials.

Plus 7 convenience methods directly on `RedTeamClient` for dashboard, quota, error logs, and sentiment.

Expand Down Expand Up @@ -464,6 +468,39 @@ console.log(creds.token); // JWT token
console.log(creds.expiry); // expiration timestamp
```

## Network Broker

The network broker routes attack traffic to targets that live behind a private network. A target references a broker channel by its UUID (`network_broker_channel_uuid`); the `networkBroker` sub-client lets you **discover and manage those channels from code** instead of copying UUIDs from the console.

It uses a distinct base URL (the network broker data plane) but shares the Red Team OAuth credentials. Override the endpoint with the `networkBrokerEndpoint` constructor option or the `PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT` environment variable.

```ts
// List channels, optionally filtering by one or more statuses.
const { data } = await client.networkBroker.listChannels({
status: ['ONLINE', 'DRAFT'],
search: 'prod',
limit: 20,
});
for (const c of data) {
console.log(c.uuid, c.name, c.status); // feed c.uuid into target.network_broker_channel_uuid
}

// Create a channel.
const channel = await client.networkBroker.createChannel({
name: 'prod-broker',
description: 'Production network broker channel',
});

// Get, update, and inspect infrastructure stats.
const detail = await client.networkBroker.getChannel(channel.uuid!);
await client.networkBroker.updateChannel(channel.uuid!, { description: 'Updated description' });

const stats = await client.networkBroker.getChannelStats();
console.log(stats.online_channel_count, stats.total_channel_count);
```

Channel statuses are `ONLINE`, `OFFLINE`, and `DRAFT` (see the `ChannelStatus` enum).

## Custom Attacks

Author your own attack content when the built-in libraries don't cover a domain-specific risk. The hierarchy is: a **prompt set** holds many **prompts**; **properties** are optional tags (e.g. `severity`, `category`) you can attach for organization. Reference an active prompt set's UUID in a scan's `job_metadata.custom_prompt_sets` to run it.
Expand Down
59 changes: 59 additions & 0 deletions docs-site/examples/red-team-network-broker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { RedTeamClient, AISecSDKException } from '@cdot65/prisma-airs-sdk';

async function main() {
// Uses PANW_RED_TEAM_* env vars (falls back to PANW_MGMT_*) for auth.
// Override the broker endpoint with PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT or:
// new RedTeamClient({ networkBrokerEndpoint: 'https://...' })
const client = new RedTeamClient();

try {
// --- LIST CHANNELS ---
console.log('Listing network broker channels...');
const channels = await client.networkBroker.listChannels({
status: ['ONLINE', 'DRAFT'],
limit: 20,
});
console.log(`Found ${channels.pagination?.total_items ?? 0} channels:`);
for (const c of channels.data) {
console.log(` - ${c.uuid}: ${c.name} [${c.status}]`);
}

// --- CREATE A CHANNEL ---
console.log('\nCreating a channel...');
const created = await client.networkBroker.createChannel({
name: 'sdk-example-broker',
description: 'Created from the SDK network broker example',
});
console.log(' Created:', created.uuid, created.name, created.status);

// --- GET / UPDATE ---
if (created.uuid) {
const detail = await client.networkBroker.getChannel(created.uuid);
console.log('\nChannel detail:', detail.name, detail.status);

const updated = await client.networkBroker.updateChannel(created.uuid, {
description: 'Updated from the SDK example',
});
console.log(' Updated description:', updated.description);

// The channel UUID is what a target references via network_broker_channel_uuid.
console.log(' Use this UUID for target.network_broker_channel_uuid:', created.uuid);
}

// --- STATS ---
console.log('\nGetting channel stats...');
const stats = await client.networkBroker.getChannelStats();
console.log(' Broker server:', stats.broker_server);
console.log(' Online channels:', stats.online_channel_count);
console.log(' Total channels:', stats.total_channel_count);
} catch (error) {
if (error instanceof AISecSDKException) {
console.error('Error:', error.message);
console.error('Type:', error.errorType);
} else {
throw error;
}
}
}

main().catch(console.error);
7 changes: 7 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ export const DEFAULT_RED_TEAM_DATA_ENDPOINT =
'https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane';
export const DEFAULT_RED_TEAM_MGMT_ENDPOINT =
'https://api.sase.paloaltonetworks.com/ai-red-teaming/mgmt-plane';
export const DEFAULT_RED_TEAM_NETWORK_BROKER_ENDPOINT =
'https://api.sase.paloaltonetworks.com/ai-red-teaming/data-plane/network-broker';

// Red Team env vars
export const RED_TEAM_CLIENT_ID = 'PANW_RED_TEAM_CLIENT_ID';
Expand All @@ -134,6 +136,7 @@ export const RED_TEAM_TSG_ID = 'PANW_RED_TEAM_TSG_ID';
export const RED_TEAM_DATA_ENDPOINT = 'PANW_RED_TEAM_DATA_ENDPOINT';
export const RED_TEAM_MGMT_ENDPOINT = 'PANW_RED_TEAM_MGMT_ENDPOINT';
export const RED_TEAM_TOKEN_ENDPOINT = 'PANW_RED_TEAM_TOKEN_ENDPOINT';
export const RED_TEAM_NETWORK_BROKER_ENDPOINT = 'PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT';

// API paths — red team data plane
export const RED_TEAM_SCAN_PATH = '/v1/scan';
Expand All @@ -156,3 +159,7 @@ export const RED_TEAM_INSTANCES_PATH = '/v1/instances';
export const RED_TEAM_REGISTRY_CREDENTIALS_PATH = '/v1/registry-credentials';
export const RED_TEAM_CUSTOM_ATTACK_PATH = '/v1/custom-attack';
export const RED_TEAM_MGMT_DASHBOARD_PATH = '/v1/dashboard/overview';

// API paths — red team network broker (distinct data-plane base URL)
export const RED_TEAM_CHANNELS_PATH = '/v1/channels';
export const RED_TEAM_CHANNELS_STATS_PATH = '/v1/channels/stats';
1 change: 1 addition & 0 deletions src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,4 @@ export * from './model-security-enums.js';
export * from './model-security.js';
export * from './red-team-enums.js';
export * from './red-team.js';
export * from './red-team-network-broker.js';
8 changes: 8 additions & 0 deletions src/models/red-team-enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export const BrandSubCategory = {
} as const;
export type BrandSubCategory = (typeof BrandSubCategory)[keyof typeof BrandSubCategory];

/** Network broker channel lifecycle status. */
export const ChannelStatus = {
ONLINE: 'ONLINE',
OFFLINE: 'OFFLINE',
DRAFT: 'DRAFT',
} as const;
export type ChannelStatus = (typeof ChannelStatus)[keyof typeof ChannelStatus];

/** Compliance framework subcategories. */
export const ComplianceSubCategory = {
OWASP: 'OWASP',
Expand Down
81 changes: 81 additions & 0 deletions src/models/red-team-network-broker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// src/models/red-team-network-broker.ts — Zod schemas + types for the AIRS Red Teaming
// Network Broker channel API. Response schemas use `.passthrough()` for forward compatibility.

import { z } from 'zod';
import { ChannelStatus } from './red-team-enums.js';

// ---------------------------------------------------------------------------
// Channel status
// ---------------------------------------------------------------------------

/** Channel lifecycle status. Accepts the upstream values `ONLINE`, `OFFLINE`, `DRAFT`. */
export const ChannelStatusSchema = z.nativeEnum(ChannelStatus);
export type ChannelStatusType = z.infer<typeof ChannelStatusSchema>;

// ---------------------------------------------------------------------------
// Requests
// ---------------------------------------------------------------------------

/** Request body for creating a network broker channel. */
export const CreateChannelRequestSchema = z
.object({
name: z.string(),
description: z.string().optional(),
})
.passthrough();
export type CreateChannelRequest = z.infer<typeof CreateChannelRequestSchema>;

/** Request body for updating a network broker channel. */
export const UpdateChannelRequestSchema = z
.object({
name: z.string().optional(),
description: z.string().optional(),
})
.passthrough();
export type UpdateChannelRequest = z.infer<typeof UpdateChannelRequestSchema>;

// ---------------------------------------------------------------------------
// Responses
// ---------------------------------------------------------------------------

/** A network broker channel. */
export const ChannelSchema = z
.object({
uuid: z.string().optional(),
name: z.string().nullable().optional(),
description: z.string().nullable().optional(),
// Kept as a plain string (not the enum) so unknown upstream statuses never fail parsing.
status: z.string().nullable().optional(),
created_at: z.string().nullable().optional(),
updated_at: z.string().nullable().optional(),
})
.passthrough();
export type Channel = z.infer<typeof ChannelSchema>;

/** Pagination metadata for a channel list response. */
export const ChannelListPaginationSchema = z
.object({ total_items: z.number().int().nullable().optional() })
.passthrough();
export type ChannelListPagination = z.infer<typeof ChannelListPaginationSchema>;

/** Paginated list of network broker channels. */
export const ChannelListResponseSchema = z
.object({
pagination: ChannelListPaginationSchema.optional(),
data: z.array(ChannelSchema).default([]),
})
.passthrough();
export type ChannelListResponse = z.infer<typeof ChannelListResponseSchema>;

/** Network broker infrastructure and channel-count stats. */
export const ChannelStatsSchema = z
.object({
broker_server: z.string().nullable().optional(),
registry: z.string().nullable().optional(),
chart: z.string().nullable().optional(),
image: z.string().nullable().optional(),
online_channel_count: z.number().int().nullable().optional(),
total_channel_count: z.number().int().nullable().optional(),
})
.passthrough();
export type ChannelStats = z.infer<typeof ChannelStatsSchema>;
16 changes: 16 additions & 0 deletions src/red-team/client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
DEFAULT_RED_TEAM_DATA_ENDPOINT,
DEFAULT_RED_TEAM_MGMT_ENDPOINT,
DEFAULT_RED_TEAM_NETWORK_BROKER_ENDPOINT,
RED_TEAM_DATA_ENDPOINT,
RED_TEAM_MGMT_ENDPOINT,
RED_TEAM_NETWORK_BROKER_ENDPOINT,
RED_TEAM_DASHBOARD_PATH,
RED_TEAM_QUOTA_PATH,
RED_TEAM_ERROR_LOG_PATH,
Expand All @@ -22,6 +24,7 @@ import { RedTeamTargetsClient } from './targets-client.js';
import { RedTeamCustomAttacksClient } from './custom-attacks-client.js';
import { RedTeamEulaClient } from './eula-client.js';
import { RedTeamInstancesClient } from './instances-client.js';
import { RedTeamNetworkBrokerClient } from './network-broker-client.js';
import {
ScanStatisticsResponseSchema,
ScoreTrendResponseSchema,
Expand Down Expand Up @@ -50,6 +53,8 @@ export interface RedTeamClientOptions {
dataEndpoint?: string;
/** Management plane endpoint URL. Falls back to `PANW_RED_TEAM_MGMT_ENDPOINT`. */
mgmtEndpoint?: string;
/** Network broker endpoint URL. Falls back to `PANW_RED_TEAM_NETWORK_BROKER_ENDPOINT`. */
networkBrokerEndpoint?: string;
/** OAuth2 token endpoint URL. Falls back to `PANW_RED_TEAM_TOKEN_ENDPOINT`, then `PANW_MGMT_TOKEN_ENDPOINT`. */
tokenEndpoint?: string;
/** Max retry attempts (0-5). Defaults to 5. */
Expand Down Expand Up @@ -85,6 +90,8 @@ export class RedTeamClient {
public readonly eula: RedTeamEulaClient;
/** Management plane instance/licensing operations. */
public readonly instances: RedTeamInstancesClient;
/** Network broker channel operations (distinct network broker base URL). */
public readonly networkBroker: RedTeamNetworkBrokerClient;

private readonly dataEndpoint: string;
private readonly mgmtEndpoint: string;
Expand All @@ -96,6 +103,10 @@ export class RedTeamClient {
opts.dataEndpoint ?? process.env[RED_TEAM_DATA_ENDPOINT] ?? DEFAULT_RED_TEAM_DATA_ENDPOINT;
const mgmtEndpoint =
opts.mgmtEndpoint ?? process.env[RED_TEAM_MGMT_ENDPOINT] ?? DEFAULT_RED_TEAM_MGMT_ENDPOINT;
const networkBrokerEndpoint =
opts.networkBrokerEndpoint ??
process.env[RED_TEAM_NETWORK_BROKER_ENDPOINT] ??
DEFAULT_RED_TEAM_NETWORK_BROKER_ENDPOINT;

const { oauthClient, numRetries } = resolveOAuthConfig({
clientId: opts.clientId,
Expand Down Expand Up @@ -129,6 +140,11 @@ export class RedTeamClient {
});
this.eula = new RedTeamEulaClient({ baseUrl: mgmtEndpoint, auth, numRetries });
this.instances = new RedTeamInstancesClient({ baseUrl: mgmtEndpoint, auth, numRetries });
this.networkBroker = new RedTeamNetworkBrokerClient({
baseUrl: networkBrokerEndpoint,
auth,
numRetries,
});
}

// -----------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions src/red-team/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ export {
} from './custom-attacks-client.js';
export { RedTeamEulaClient, type RedTeamEulaClientOptions } from './eula-client.js';
export { RedTeamInstancesClient, type RedTeamInstancesClientOptions } from './instances-client.js';
export {
RedTeamNetworkBrokerClient,
type RedTeamNetworkBrokerClientOptions,
type ChannelListOptions,
} from './network-broker-client.js';
Loading
Loading