vix11 1.0.0
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 +188 -0
- package/dist/client.d.ts +71 -0
- package/dist/client.js +148 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/middleware.d.ts +36 -0
- package/dist/middleware.js +58 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.js +22 -0
- package/package.json +16 -0
- package/src/client.ts +211 -0
- package/src/index.ts +32 -0
- package/src/middleware.ts +88 -0
- package/src/types.ts +232 -0
- package/tests/client.test.ts +280 -0
- package/tests/middleware.test.ts +193 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Vix11 Node.js SDK
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for the [Vix11](../../README.md) AI agent security platform. Provides a typed client for the Vix11 API and drop-in Express middleware.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vix11
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { Vix11Client } from 'vix11';
|
|
15
|
+
|
|
16
|
+
const client = new Vix11Client({
|
|
17
|
+
baseUrl: 'https://vix11.example.com',
|
|
18
|
+
agentId: 'my-agent',
|
|
19
|
+
apiKey: 'your-api-key',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const result = await client.detect({
|
|
23
|
+
endpoint: '/api/chat',
|
|
24
|
+
payload: '{"prompt": "hello"}',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (result.detection.action === 'block') {
|
|
28
|
+
console.log('Request blocked:', result.detection);
|
|
29
|
+
} else {
|
|
30
|
+
console.log('Request allowed');
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Express Middleware
|
|
35
|
+
|
|
36
|
+
The SDK includes middleware that automatically intercepts every incoming request, runs it through the Vix11 detection pipeline, and blocks or throttles suspicious traffic.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import express from 'express';
|
|
40
|
+
import { vix11Middleware } from 'vix11';
|
|
41
|
+
|
|
42
|
+
const app = express();
|
|
43
|
+
|
|
44
|
+
app.use(vix11Middleware({
|
|
45
|
+
baseUrl: 'https://vix11.example.com',
|
|
46
|
+
agentId: 'my-agent',
|
|
47
|
+
apiKey: 'your-api-key',
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
app.post('/api/chat', (req, res) => {
|
|
51
|
+
// req.vix11 contains the detection result for downstream use
|
|
52
|
+
console.log('Detection score:', req.vix11);
|
|
53
|
+
res.json({ reply: 'Hello!' });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
app.listen(3000);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Middleware behavior:**
|
|
60
|
+
|
|
61
|
+
- `block` action: responds with `403` and a JSON error body.
|
|
62
|
+
- `throttle` action: responds with `429` and a `retryAfterMs` field.
|
|
63
|
+
- `allow` / `shadow-ban`: calls `next()` and attaches the detection result to `req.vix11`.
|
|
64
|
+
- On detection failure, the middleware **fails open** and forwards the error to the Express error handler.
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
|
|
68
|
+
### `new Vix11Client(config)`
|
|
69
|
+
|
|
70
|
+
Creates a new client instance.
|
|
71
|
+
|
|
72
|
+
### `client.detect(payload): Promise<DetectResponse>`
|
|
73
|
+
|
|
74
|
+
Run the 11-layer detection pipeline against a request.
|
|
75
|
+
|
|
76
|
+
**Parameters:**
|
|
77
|
+
|
|
78
|
+
| Field | Type | Required | Description |
|
|
79
|
+
|------------|----------|----------|------------------------------------|
|
|
80
|
+
| `agentId` | `string` | Yes | Agent identifier |
|
|
81
|
+
| `endpoint` | `string` | Yes | Target endpoint path |
|
|
82
|
+
| `payload` | `string` | No | Request body as a string |
|
|
83
|
+
| `ip` | `string` | No | Client IP address |
|
|
84
|
+
|
|
85
|
+
**Returns:** `DetectResponse` containing the detection result with `action` (`allow`, `block`, `throttle`, `shadow-ban`) and layer scores.
|
|
86
|
+
|
|
87
|
+
### `client.shield(payload): Promise<ShieldResponse>`
|
|
88
|
+
|
|
89
|
+
Run the shielded-request flow, which combines passport verification with the detection pipeline.
|
|
90
|
+
|
|
91
|
+
**Parameters:** Same as `detect`.
|
|
92
|
+
|
|
93
|
+
**Returns:** `ShieldResponse` with passport verification status and detection result.
|
|
94
|
+
|
|
95
|
+
### `client.getAnalyticsSummary(from?, to?): Promise<AnalyticsSummary>`
|
|
96
|
+
|
|
97
|
+
Retrieve the analytics summary for a time range, including the "$X saved" distillation metric.
|
|
98
|
+
|
|
99
|
+
**Parameters:**
|
|
100
|
+
|
|
101
|
+
| Field | Type | Required | Description |
|
|
102
|
+
|--------|----------|----------|------------------------------|
|
|
103
|
+
| `from` | `number` | No | Start timestamp (epoch ms) |
|
|
104
|
+
| `to` | `number` | No | End timestamp (epoch ms) |
|
|
105
|
+
|
|
106
|
+
### `client.getAnalyticsEvents(page?, limit?): Promise<PaginatedEvents>`
|
|
107
|
+
|
|
108
|
+
Retrieve paginated detection events.
|
|
109
|
+
|
|
110
|
+
**Parameters:**
|
|
111
|
+
|
|
112
|
+
| Field | Type | Required | Description |
|
|
113
|
+
|---------|----------|----------|------------------------|
|
|
114
|
+
| `page` | `number` | No | Page number |
|
|
115
|
+
| `limit` | `number` | No | Events per page |
|
|
116
|
+
|
|
117
|
+
### `client.getComplianceReport(): Promise<ComplianceReport>`
|
|
118
|
+
|
|
119
|
+
Retrieve the current OWASP ASI compliance report.
|
|
120
|
+
|
|
121
|
+
### `client.getDetectionStats(): Promise<DetectionStats>`
|
|
122
|
+
|
|
123
|
+
Retrieve detection pipeline statistics, including per-layer performance and thresholds.
|
|
124
|
+
|
|
125
|
+
### `client.health(): Promise<{ status: string }>`
|
|
126
|
+
|
|
127
|
+
Check API health. Returns `{ status: "ok" }` when the service is operational.
|
|
128
|
+
|
|
129
|
+
## Error Handling
|
|
130
|
+
|
|
131
|
+
All API errors are thrown as `Vix11Error` instances.
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
import { Vix11Error } from 'vix11';
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
await client.detect({ endpoint: '/api/chat', payload: '...' });
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (err instanceof Vix11Error) {
|
|
140
|
+
console.error('Vix11 API error:', err.message);
|
|
141
|
+
console.error('HTTP status:', err.status);
|
|
142
|
+
console.error('Response body:', err.body);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**`Vix11Error` properties:**
|
|
148
|
+
|
|
149
|
+
| Property | Type | Description |
|
|
150
|
+
|-----------|-----------|--------------------------------------------------|
|
|
151
|
+
| `message` | `string` | Human-readable error message |
|
|
152
|
+
| `status` | `number` | HTTP status code (0 for timeouts) |
|
|
153
|
+
| `body` | `unknown` | Raw response body, if available |
|
|
154
|
+
|
|
155
|
+
Timeout errors have `status` set to `0` and a message indicating the configured timeout value.
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
| Option | Type | Required | Default | Description |
|
|
160
|
+
|-----------|----------|----------|---------|-----------------------------------------------|
|
|
161
|
+
| `baseUrl` | `string` | Yes | -- | Base URL of the Vix11 API |
|
|
162
|
+
| `agentId` | `string` | Yes | -- | Agent identifier sent with every request |
|
|
163
|
+
| `apiKey` | `string` | No | -- | API key for authenticated requests |
|
|
164
|
+
| `timeout` | `number` | No | `5000` | Request timeout in milliseconds |
|
|
165
|
+
|
|
166
|
+
## TypeScript
|
|
167
|
+
|
|
168
|
+
The SDK is written in TypeScript and exports all types used across the API:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import type {
|
|
172
|
+
Vix11Config,
|
|
173
|
+
DetectRequest,
|
|
174
|
+
DetectResponse,
|
|
175
|
+
ShieldRequest,
|
|
176
|
+
ShieldResponse,
|
|
177
|
+
AnalyticsSummary,
|
|
178
|
+
PaginatedEvents,
|
|
179
|
+
ComplianceReport,
|
|
180
|
+
DetectionStats,
|
|
181
|
+
} from 'vix11';
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
No `@types` package is needed. Type definitions are bundled with the package.
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vix11 SDK Client
|
|
3
|
+
*
|
|
4
|
+
* Provides a typed interface for interacting with the Vix11 API.
|
|
5
|
+
* Uses native fetch (no external dependencies).
|
|
6
|
+
*/
|
|
7
|
+
import type { DetectRequest, DetectResponse, ShieldRequest, ShieldResponse, AnalyticsSummary, PaginatedEvents, ComplianceReport, DetectionStats, TrustScore, HealthResponse, SignedPassport, PassportCapabilities } from './types';
|
|
8
|
+
/** Configuration for the Vix11 client. */
|
|
9
|
+
export interface Vix11Config {
|
|
10
|
+
/** Base URL of the Vix11 API (e.g. "https://vix11.example.com"). */
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
/** Agent identifier sent with every request. */
|
|
13
|
+
agentId: string;
|
|
14
|
+
/** Optional API key for authenticated requests. */
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
/** Request timeout in milliseconds (default: 5000). */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class Vix11Client {
|
|
20
|
+
private readonly baseUrl;
|
|
21
|
+
private readonly agentId;
|
|
22
|
+
private readonly apiKey?;
|
|
23
|
+
private readonly timeout;
|
|
24
|
+
constructor(config: Vix11Config);
|
|
25
|
+
/** Run the detection pipeline against a request payload. */
|
|
26
|
+
detect(payload: DetectRequest): Promise<DetectResponse>;
|
|
27
|
+
/** Run the shielded-request flow (passport verification + detection). */
|
|
28
|
+
shield(payload: ShieldRequest): Promise<ShieldResponse>;
|
|
29
|
+
/** Retrieve the analytics summary for a time range. */
|
|
30
|
+
getAnalyticsSummary(from?: number, to?: number): Promise<AnalyticsSummary>;
|
|
31
|
+
/** Retrieve paginated analytics events. */
|
|
32
|
+
getAnalyticsEvents(page?: number, limit?: number): Promise<PaginatedEvents>;
|
|
33
|
+
/** Retrieve the current compliance report. */
|
|
34
|
+
getComplianceReport(): Promise<ComplianceReport>;
|
|
35
|
+
/** Retrieve detection pipeline stats (layers, thresholds). */
|
|
36
|
+
getDetectionStats(): Promise<DetectionStats>;
|
|
37
|
+
/** Get the Agent Trust Score for a specific agent. */
|
|
38
|
+
getTrustScore(agentId: string): Promise<TrustScore>;
|
|
39
|
+
/** Issue a new KYA passport for an agent. */
|
|
40
|
+
issuePassport(params: {
|
|
41
|
+
agentId: string;
|
|
42
|
+
ownerId: string;
|
|
43
|
+
capabilities: PassportCapabilities;
|
|
44
|
+
ttlSeconds?: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
passport: SignedPassport;
|
|
47
|
+
meta: {
|
|
48
|
+
latencyMs: number;
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
51
|
+
/** Verify a signed passport. */
|
|
52
|
+
verifyPassport(passport: SignedPassport): Promise<{
|
|
53
|
+
valid: boolean;
|
|
54
|
+
reason: string;
|
|
55
|
+
payload?: SignedPassport['payload'];
|
|
56
|
+
meta: {
|
|
57
|
+
latencyMs: number;
|
|
58
|
+
};
|
|
59
|
+
}>;
|
|
60
|
+
/** Revoke a passport by ID. */
|
|
61
|
+
revokePassport(passportId: string, reason: string): Promise<{
|
|
62
|
+
revoked: boolean;
|
|
63
|
+
passportId: string;
|
|
64
|
+
}>;
|
|
65
|
+
/** Check API health with KV connectivity probes. */
|
|
66
|
+
health(): Promise<HealthResponse>;
|
|
67
|
+
private buildHeaders;
|
|
68
|
+
private request;
|
|
69
|
+
private get;
|
|
70
|
+
private post;
|
|
71
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vix11 SDK Client
|
|
3
|
+
*
|
|
4
|
+
* Provides a typed interface for interacting with the Vix11 API.
|
|
5
|
+
* Uses native fetch (no external dependencies).
|
|
6
|
+
*/
|
|
7
|
+
import { Vix11Error } from './types';
|
|
8
|
+
export class Vix11Client {
|
|
9
|
+
baseUrl;
|
|
10
|
+
agentId;
|
|
11
|
+
apiKey;
|
|
12
|
+
timeout;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
// Strip trailing slash for consistent URL building.
|
|
15
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, '');
|
|
16
|
+
this.agentId = config.agentId;
|
|
17
|
+
this.apiKey = config.apiKey;
|
|
18
|
+
this.timeout = config.timeout ?? 5000;
|
|
19
|
+
}
|
|
20
|
+
// -----------------------------------------------------------------------
|
|
21
|
+
// Detection
|
|
22
|
+
// -----------------------------------------------------------------------
|
|
23
|
+
/** Run the detection pipeline against a request payload. */
|
|
24
|
+
async detect(payload) {
|
|
25
|
+
return this.post('/v1/detect', payload);
|
|
26
|
+
}
|
|
27
|
+
/** Run the shielded-request flow (passport verification + detection). */
|
|
28
|
+
async shield(payload) {
|
|
29
|
+
return this.post('/v1/shield', payload);
|
|
30
|
+
}
|
|
31
|
+
// -----------------------------------------------------------------------
|
|
32
|
+
// Analytics
|
|
33
|
+
// -----------------------------------------------------------------------
|
|
34
|
+
/** Retrieve the analytics summary for a time range. */
|
|
35
|
+
async getAnalyticsSummary(from, to) {
|
|
36
|
+
const params = new URLSearchParams();
|
|
37
|
+
if (from !== undefined)
|
|
38
|
+
params.set('from', String(from));
|
|
39
|
+
if (to !== undefined)
|
|
40
|
+
params.set('to', String(to));
|
|
41
|
+
const qs = params.toString();
|
|
42
|
+
return this.get(`/v1/analytics/summary${qs ? `?${qs}` : ''}`);
|
|
43
|
+
}
|
|
44
|
+
/** Retrieve paginated analytics events. */
|
|
45
|
+
async getAnalyticsEvents(page, limit) {
|
|
46
|
+
const params = new URLSearchParams();
|
|
47
|
+
if (page !== undefined)
|
|
48
|
+
params.set('page', String(page));
|
|
49
|
+
if (limit !== undefined)
|
|
50
|
+
params.set('limit', String(limit));
|
|
51
|
+
const qs = params.toString();
|
|
52
|
+
return this.get(`/v1/analytics/events${qs ? `?${qs}` : ''}`);
|
|
53
|
+
}
|
|
54
|
+
// -----------------------------------------------------------------------
|
|
55
|
+
// Compliance
|
|
56
|
+
// -----------------------------------------------------------------------
|
|
57
|
+
/** Retrieve the current compliance report. */
|
|
58
|
+
async getComplianceReport() {
|
|
59
|
+
return this.get('/v1/compliance/report');
|
|
60
|
+
}
|
|
61
|
+
// -----------------------------------------------------------------------
|
|
62
|
+
// Stats
|
|
63
|
+
// -----------------------------------------------------------------------
|
|
64
|
+
/** Retrieve detection pipeline stats (layers, thresholds). */
|
|
65
|
+
async getDetectionStats() {
|
|
66
|
+
return this.get('/v1/detection/stats');
|
|
67
|
+
}
|
|
68
|
+
// -----------------------------------------------------------------------
|
|
69
|
+
// Trust
|
|
70
|
+
// -----------------------------------------------------------------------
|
|
71
|
+
/** Get the Agent Trust Score for a specific agent. */
|
|
72
|
+
async getTrustScore(agentId) {
|
|
73
|
+
return this.get(`/v1/trust/${encodeURIComponent(agentId)}`);
|
|
74
|
+
}
|
|
75
|
+
// -----------------------------------------------------------------------
|
|
76
|
+
// Passport
|
|
77
|
+
// -----------------------------------------------------------------------
|
|
78
|
+
/** Issue a new KYA passport for an agent. */
|
|
79
|
+
async issuePassport(params) {
|
|
80
|
+
return this.post('/v1/passport/issue', params);
|
|
81
|
+
}
|
|
82
|
+
/** Verify a signed passport. */
|
|
83
|
+
async verifyPassport(passport) {
|
|
84
|
+
return this.post('/v1/passport/verify', passport);
|
|
85
|
+
}
|
|
86
|
+
/** Revoke a passport by ID. */
|
|
87
|
+
async revokePassport(passportId, reason) {
|
|
88
|
+
return this.post('/v1/passport/revoke', { passportId, reason });
|
|
89
|
+
}
|
|
90
|
+
// -----------------------------------------------------------------------
|
|
91
|
+
// Health
|
|
92
|
+
// -----------------------------------------------------------------------
|
|
93
|
+
/** Check API health with KV connectivity probes. */
|
|
94
|
+
async health() {
|
|
95
|
+
return this.get('/v1/health');
|
|
96
|
+
}
|
|
97
|
+
// -----------------------------------------------------------------------
|
|
98
|
+
// Internal helpers
|
|
99
|
+
// -----------------------------------------------------------------------
|
|
100
|
+
buildHeaders() {
|
|
101
|
+
const headers = {
|
|
102
|
+
'Content-Type': 'application/json',
|
|
103
|
+
'X-Vix11-Agent-Id': this.agentId,
|
|
104
|
+
};
|
|
105
|
+
if (this.apiKey) {
|
|
106
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
107
|
+
}
|
|
108
|
+
return headers;
|
|
109
|
+
}
|
|
110
|
+
async request(method, path, body) {
|
|
111
|
+
const url = `${this.baseUrl}${path}`;
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
114
|
+
try {
|
|
115
|
+
const response = await fetch(url, {
|
|
116
|
+
method,
|
|
117
|
+
headers: this.buildHeaders(),
|
|
118
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
119
|
+
signal: controller.signal,
|
|
120
|
+
});
|
|
121
|
+
const responseBody = await response.json().catch(() => null);
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const message = (responseBody && typeof responseBody === 'object' && 'error' in responseBody
|
|
124
|
+
? String(responseBody.error)
|
|
125
|
+
: undefined) ?? `Request failed with status ${response.status}`;
|
|
126
|
+
throw new Vix11Error(message, response.status, responseBody);
|
|
127
|
+
}
|
|
128
|
+
return responseBody;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (err instanceof Vix11Error)
|
|
132
|
+
throw err;
|
|
133
|
+
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
134
|
+
throw new Vix11Error(`Request timed out after ${this.timeout}ms`, 0);
|
|
135
|
+
}
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
get(path) {
|
|
143
|
+
return this.request('GET', path);
|
|
144
|
+
}
|
|
145
|
+
post(path, body) {
|
|
146
|
+
return this.request('POST', path, body);
|
|
147
|
+
}
|
|
148
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vix11 SDK — AI Agent Security
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export { Vix11Client } from './client';
|
|
7
|
+
export type { Vix11Config } from './client';
|
|
8
|
+
export { vix11Middleware } from './middleware';
|
|
9
|
+
export type { RequestHandler } from './middleware';
|
|
10
|
+
export { Vix11Error, type LayerScore, type DetectionMeta, type DetectRequest, type DetectResponse, type ShieldRequest, type ShieldResponse, type PassportCapabilities, type PassportPayload, type SignedPassport, type AnalyticsSummary, type PaginatedEvents, type DistillationEvent, type ComplianceReport, type DetectionStats, type TrustTier, type TrustScore, type HealthCheck, type HealthResponse, } from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vix11 Express Middleware
|
|
3
|
+
*
|
|
4
|
+
* Drop-in Express middleware that intercepts incoming requests,
|
|
5
|
+
* runs them through the Vix11 detection pipeline, and blocks or
|
|
6
|
+
* throttles suspicious traffic automatically.
|
|
7
|
+
*/
|
|
8
|
+
import type { Vix11Config } from './client';
|
|
9
|
+
import type { DetectResponse } from './types';
|
|
10
|
+
/** Minimal Express-compatible types so we avoid a dependency on @types/express. */
|
|
11
|
+
interface Request {
|
|
12
|
+
ip?: string;
|
|
13
|
+
path?: string;
|
|
14
|
+
method?: string;
|
|
15
|
+
body?: unknown;
|
|
16
|
+
headers: Record<string, string | string[] | undefined>;
|
|
17
|
+
vix11?: DetectResponse['detection'];
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
interface Response {
|
|
21
|
+
status(code: number): Response;
|
|
22
|
+
json(body: unknown): void;
|
|
23
|
+
}
|
|
24
|
+
type NextFunction = (err?: unknown) => void;
|
|
25
|
+
/** Express-compatible RequestHandler signature. */
|
|
26
|
+
export type RequestHandler = (req: Request, res: Response, next: NextFunction) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Create Express middleware that runs every incoming request through
|
|
29
|
+
* Vix11 detection.
|
|
30
|
+
*
|
|
31
|
+
* - If the action is `block`, responds with 403.
|
|
32
|
+
* - If the action is `throttle`, responds with 429.
|
|
33
|
+
* - Otherwise calls `next()` and attaches the detection result to `req.vix11`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function vix11Middleware(config: Vix11Config): RequestHandler;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vix11 Express Middleware
|
|
3
|
+
*
|
|
4
|
+
* Drop-in Express middleware that intercepts incoming requests,
|
|
5
|
+
* runs them through the Vix11 detection pipeline, and blocks or
|
|
6
|
+
* throttles suspicious traffic automatically.
|
|
7
|
+
*/
|
|
8
|
+
import { Vix11Client } from './client';
|
|
9
|
+
/**
|
|
10
|
+
* Create Express middleware that runs every incoming request through
|
|
11
|
+
* Vix11 detection.
|
|
12
|
+
*
|
|
13
|
+
* - If the action is `block`, responds with 403.
|
|
14
|
+
* - If the action is `throttle`, responds with 429.
|
|
15
|
+
* - Otherwise calls `next()` and attaches the detection result to `req.vix11`.
|
|
16
|
+
*/
|
|
17
|
+
export function vix11Middleware(config) {
|
|
18
|
+
const client = new Vix11Client(config);
|
|
19
|
+
return (req, res, next) => {
|
|
20
|
+
const agentIdHeader = req.headers['x-vix11-agent-id'];
|
|
21
|
+
const agentId = (typeof agentIdHeader === 'string' ? agentIdHeader : undefined) ?? config.agentId;
|
|
22
|
+
const ip = (typeof req.headers['x-forwarded-for'] === 'string'
|
|
23
|
+
? req.headers['x-forwarded-for'].split(',')[0]?.trim()
|
|
24
|
+
: undefined) ?? req.ip ?? '0.0.0.0';
|
|
25
|
+
client
|
|
26
|
+
.detect({
|
|
27
|
+
agentId,
|
|
28
|
+
endpoint: req.path ?? '/',
|
|
29
|
+
payload: req.body ? JSON.stringify(req.body) : undefined,
|
|
30
|
+
ip,
|
|
31
|
+
})
|
|
32
|
+
.then((result) => {
|
|
33
|
+
// Attach to request for downstream handlers.
|
|
34
|
+
req.vix11 = result.detection;
|
|
35
|
+
switch (result.detection.action) {
|
|
36
|
+
case 'block':
|
|
37
|
+
res.status(403).json({
|
|
38
|
+
error: 'Request blocked by Vix11 security policy',
|
|
39
|
+
action: 'block',
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
case 'throttle':
|
|
43
|
+
res.status(429).json({
|
|
44
|
+
error: 'Request throttled by Vix11 security policy',
|
|
45
|
+
action: 'throttle',
|
|
46
|
+
retryAfterMs: 5000,
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
default:
|
|
50
|
+
next();
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.catch((err) => {
|
|
54
|
+
// On detection failure, fail open (let request through) and forward the error.
|
|
55
|
+
next(err);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
}
|