@socwarden/sdk 1.0.0-alpha.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 +131 -0
- package/dist/builder.d.ts +90 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +182 -0
- package/dist/builder.js.map +1 -0
- package/dist/client.d.ts +74 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +333 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +13 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +15 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +42 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +71 -0
- package/dist/middleware.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @socwarden/sdk
|
|
2
|
+
|
|
3
|
+
Node.js/TypeScript SDK for [SOCWarden](https://socwarden.io) security event tracking.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @socwarden/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SOCWardenClient } from '@socwarden/sdk';
|
|
15
|
+
|
|
16
|
+
const soc = new SOCWardenClient({
|
|
17
|
+
apiKey: 'sk_live_...',
|
|
18
|
+
// endpoint: 'https://ingest.socwarden.io', // default
|
|
19
|
+
// timeout: 5000, // default, in ms
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### track() — Named Arguments
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// Simple event
|
|
29
|
+
await soc.track('auth.login.success', { actor: userId });
|
|
30
|
+
|
|
31
|
+
// With full options
|
|
32
|
+
await soc.track('data.exported', {
|
|
33
|
+
actor: { id: user.id, email: user.email },
|
|
34
|
+
metadata: { format: 'csv', rows: 1500 },
|
|
35
|
+
resource: { type: 'Report', id: report.id },
|
|
36
|
+
ip: '203.0.113.42',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// With explicit actor fields
|
|
40
|
+
await soc.track('auth.login.failure', {
|
|
41
|
+
actorEmail: req.body.email,
|
|
42
|
+
ip: req.ip,
|
|
43
|
+
userAgent: req.get('user-agent'),
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### trackData() — Raw Object
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
await soc.trackData('auth.login.success', {
|
|
51
|
+
actor_id: user.id,
|
|
52
|
+
actor_email: user.email,
|
|
53
|
+
metadata: { role: 'admin' },
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### event() — Fluent Builder
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
await soc.event('data.exported')
|
|
61
|
+
.actor({ id: user.id, email: user.email })
|
|
62
|
+
.resource('Report', report.id)
|
|
63
|
+
.meta('format', 'csv')
|
|
64
|
+
.meta('rows', 1500)
|
|
65
|
+
.send();
|
|
66
|
+
|
|
67
|
+
// Chaining metadata
|
|
68
|
+
await soc.event('auth.mfa.enrolled')
|
|
69
|
+
.actor(user.id)
|
|
70
|
+
.metadata({ method: 'totp', provider: 'google' })
|
|
71
|
+
.severity('info')
|
|
72
|
+
.send();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Express Middleware
|
|
76
|
+
|
|
77
|
+
The middleware captures request context (IP, user-agent, path, etc.) and attaches it to every event sent during the request lifecycle.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import express from 'express';
|
|
81
|
+
import { SOCWardenClient, socwardenMiddleware } from '@socwarden/sdk';
|
|
82
|
+
|
|
83
|
+
const soc = new SOCWardenClient({ apiKey: 'sk_live_...' });
|
|
84
|
+
const app = express();
|
|
85
|
+
|
|
86
|
+
// Attach SOCWarden middleware
|
|
87
|
+
app.use(socwardenMiddleware(soc));
|
|
88
|
+
|
|
89
|
+
app.post('/login', async (req, res) => {
|
|
90
|
+
const user = await authenticate(req.body);
|
|
91
|
+
|
|
92
|
+
if (user) {
|
|
93
|
+
// IP, user-agent, path are auto-captured from the request
|
|
94
|
+
await soc.track('auth.login.success', { actor: user.id });
|
|
95
|
+
res.json({ ok: true });
|
|
96
|
+
} else {
|
|
97
|
+
await soc.track('auth.login.failure', {
|
|
98
|
+
actorEmail: req.body.email,
|
|
99
|
+
});
|
|
100
|
+
res.status(401).json({ error: 'Invalid credentials' });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Rate Limit Handling
|
|
106
|
+
|
|
107
|
+
The SDK automatically handles 429 (rate limit) responses:
|
|
108
|
+
|
|
109
|
+
1. On receiving a 429, it backs off for the `Retry-After` duration (default: 1 hour).
|
|
110
|
+
2. During backoff, events are silently dropped to avoid overwhelming the ingestor.
|
|
111
|
+
3. Every 5 minutes, a probe request is sent to check if the quota has been restored.
|
|
112
|
+
4. On a successful probe, normal sending resumes immediately.
|
|
113
|
+
|
|
114
|
+
## Requirements
|
|
115
|
+
|
|
116
|
+
- Node.js 18+ (uses native `fetch`)
|
|
117
|
+
- No runtime dependencies
|
|
118
|
+
|
|
119
|
+
## TypeScript
|
|
120
|
+
|
|
121
|
+
All types are exported for full TypeScript support:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import type {
|
|
125
|
+
SOCWardenOptions,
|
|
126
|
+
TrackOptions,
|
|
127
|
+
ActorInput,
|
|
128
|
+
ResourceInput,
|
|
129
|
+
EventPayload,
|
|
130
|
+
} from '@socwarden/sdk';
|
|
131
|
+
```
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { SOCWardenClient } from './client';
|
|
2
|
+
import type { ActorInput, ResourceInput } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Fluent builder for constructing and sending SOCWarden events.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* await soc.event('auth.login.success')
|
|
8
|
+
* .actor({ id: 'usr_123', email: 'john@example.com' })
|
|
9
|
+
* .ip('203.0.113.42')
|
|
10
|
+
* .metadata({ mfa: true })
|
|
11
|
+
* .resource('Session', 'sess_abc')
|
|
12
|
+
* .send();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare class EventBuilder {
|
|
16
|
+
private readonly eventName;
|
|
17
|
+
private readonly client;
|
|
18
|
+
private data;
|
|
19
|
+
constructor(event: string, client: SOCWardenClient);
|
|
20
|
+
/**
|
|
21
|
+
* Set the actor (user) who triggered the event.
|
|
22
|
+
*
|
|
23
|
+
* Accepts a string ID or an object with `id` and optional `email`.
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* .actor('usr_123')
|
|
27
|
+
* .actor({ id: 'usr_123', email: 'john@example.com' })
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
actor(actorOrId: ActorInput, email?: string): this;
|
|
31
|
+
/**
|
|
32
|
+
* Set the actor ID directly.
|
|
33
|
+
*/
|
|
34
|
+
actorId(id: string): this;
|
|
35
|
+
/**
|
|
36
|
+
* Set the actor email directly.
|
|
37
|
+
*/
|
|
38
|
+
actorEmail(email: string): this;
|
|
39
|
+
/**
|
|
40
|
+
* Set the source IP address.
|
|
41
|
+
*/
|
|
42
|
+
ip(ip: string): this;
|
|
43
|
+
/**
|
|
44
|
+
* Set the User-Agent string.
|
|
45
|
+
*/
|
|
46
|
+
userAgent(ua: string): this;
|
|
47
|
+
/**
|
|
48
|
+
* Merge custom metadata key-value pairs. Can be called multiple times;
|
|
49
|
+
* values are merged (later calls override earlier keys).
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* .metadata({ role: 'admin', format: 'csv' })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
metadata(obj: Record<string, unknown>): this;
|
|
56
|
+
/**
|
|
57
|
+
* Set a single metadata key-value pair.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* .meta('role', 'admin')
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
meta(key: string, value: unknown): this;
|
|
64
|
+
/**
|
|
65
|
+
* Set the event timestamp (ISO 8601 string or Date object).
|
|
66
|
+
*/
|
|
67
|
+
timestamp(ts: string | Date): this;
|
|
68
|
+
/**
|
|
69
|
+
* Set the event severity hint for the enricher.
|
|
70
|
+
*/
|
|
71
|
+
severity(severity: string): this;
|
|
72
|
+
/**
|
|
73
|
+
* Attach the resource that was acted upon.
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* .resource('Order', 'ord_123')
|
|
77
|
+
* .resource({ type: 'Order', id: 'ord_123' })
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
resource(typeOrObj: ResourceInput, id?: string): this;
|
|
81
|
+
/**
|
|
82
|
+
* Send the event to SOCWarden.
|
|
83
|
+
*/
|
|
84
|
+
send(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Get the built data object (for testing/inspection).
|
|
87
|
+
*/
|
|
88
|
+
toObject(): Record<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,IAAI,CAA+B;gBAE/B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe;IASlD;;;;;;;;;OASG;IACH,KAAK,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAkBlD;;OAEG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKzB;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAS/B;;OAEG;IACH,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKpB;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAS3B;;;;;;;OAOG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQ5C;;;;;;OAMG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAWvC;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKlC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWhC;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAmBrD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACH,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAMpC"}
|
package/dist/builder.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventBuilder = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Fluent builder for constructing and sending SOCWarden events.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* await soc.event('auth.login.success')
|
|
9
|
+
* .actor({ id: 'usr_123', email: 'john@example.com' })
|
|
10
|
+
* .ip('203.0.113.42')
|
|
11
|
+
* .metadata({ mfa: true })
|
|
12
|
+
* .resource('Session', 'sess_abc')
|
|
13
|
+
* .send();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
class EventBuilder {
|
|
17
|
+
eventName;
|
|
18
|
+
client;
|
|
19
|
+
data = {};
|
|
20
|
+
constructor(event, client) {
|
|
21
|
+
this.eventName = event;
|
|
22
|
+
this.client = client;
|
|
23
|
+
}
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Actor
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Set the actor (user) who triggered the event.
|
|
29
|
+
*
|
|
30
|
+
* Accepts a string ID or an object with `id` and optional `email`.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* .actor('usr_123')
|
|
34
|
+
* .actor({ id: 'usr_123', email: 'john@example.com' })
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
actor(actorOrId, email) {
|
|
38
|
+
if (typeof actorOrId === 'string') {
|
|
39
|
+
this.data.actor_id = actorOrId;
|
|
40
|
+
if (email !== undefined) {
|
|
41
|
+
this.data.actor_email = email;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.data.actor_id = actorOrId.id;
|
|
46
|
+
if (actorOrId.email) {
|
|
47
|
+
this.data.actor_email = actorOrId.email;
|
|
48
|
+
}
|
|
49
|
+
if (email !== undefined) {
|
|
50
|
+
this.data.actor_email = email;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Set the actor ID directly.
|
|
57
|
+
*/
|
|
58
|
+
actorId(id) {
|
|
59
|
+
this.data.actor_id = id;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Set the actor email directly.
|
|
64
|
+
*/
|
|
65
|
+
actorEmail(email) {
|
|
66
|
+
this.data.actor_email = email;
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Request context
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
/**
|
|
73
|
+
* Set the source IP address.
|
|
74
|
+
*/
|
|
75
|
+
ip(ip) {
|
|
76
|
+
this.data.ip = ip;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Set the User-Agent string.
|
|
81
|
+
*/
|
|
82
|
+
userAgent(ua) {
|
|
83
|
+
this.data.user_agent = ua;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Metadata
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
/**
|
|
90
|
+
* Merge custom metadata key-value pairs. Can be called multiple times;
|
|
91
|
+
* values are merged (later calls override earlier keys).
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* .metadata({ role: 'admin', format: 'csv' })
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
metadata(obj) {
|
|
98
|
+
this.data.metadata = {
|
|
99
|
+
...(this.data.metadata ?? {}),
|
|
100
|
+
...obj,
|
|
101
|
+
};
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Set a single metadata key-value pair.
|
|
106
|
+
*
|
|
107
|
+
* ```ts
|
|
108
|
+
* .meta('role', 'admin')
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
meta(key, value) {
|
|
112
|
+
const existing = this.data.metadata ?? {};
|
|
113
|
+
existing[key] = value;
|
|
114
|
+
this.data.metadata = existing;
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Timestamp & severity
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
/**
|
|
121
|
+
* Set the event timestamp (ISO 8601 string or Date object).
|
|
122
|
+
*/
|
|
123
|
+
timestamp(ts) {
|
|
124
|
+
this.data.timestamp = ts instanceof Date ? ts.toISOString() : ts;
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Set the event severity hint for the enricher.
|
|
129
|
+
*/
|
|
130
|
+
severity(severity) {
|
|
131
|
+
const existing = this.data.metadata ?? {};
|
|
132
|
+
existing._severity = severity;
|
|
133
|
+
this.data.metadata = existing;
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Resource
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
/**
|
|
140
|
+
* Attach the resource that was acted upon.
|
|
141
|
+
*
|
|
142
|
+
* ```ts
|
|
143
|
+
* .resource('Order', 'ord_123')
|
|
144
|
+
* .resource({ type: 'Order', id: 'ord_123' })
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
resource(typeOrObj, id) {
|
|
148
|
+
const existing = this.data.metadata ?? {};
|
|
149
|
+
if (typeof typeOrObj === 'string') {
|
|
150
|
+
existing.resource_type = typeOrObj;
|
|
151
|
+
if (id !== undefined) {
|
|
152
|
+
existing.resource_id = id;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
existing.resource_type = typeOrObj.type;
|
|
157
|
+
existing.resource_id = typeOrObj.id;
|
|
158
|
+
}
|
|
159
|
+
this.data.metadata = existing;
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
// Send
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
/**
|
|
166
|
+
* Send the event to SOCWarden.
|
|
167
|
+
*/
|
|
168
|
+
async send() {
|
|
169
|
+
await this.client.trackData(this.eventName, this.data);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the built data object (for testing/inspection).
|
|
173
|
+
*/
|
|
174
|
+
toObject() {
|
|
175
|
+
return {
|
|
176
|
+
event: this.eventName,
|
|
177
|
+
...this.data,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
exports.EventBuilder = EventBuilder;
|
|
182
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { EventBuilder } from './builder';
|
|
2
|
+
import { SOCWardenOptions, TrackOptions } from './types';
|
|
3
|
+
export declare class SOCWardenClient {
|
|
4
|
+
private readonly apiKey;
|
|
5
|
+
private readonly endpoint;
|
|
6
|
+
private readonly timeout;
|
|
7
|
+
/** In-memory backoff state for 429 handling. */
|
|
8
|
+
private backoffUntil;
|
|
9
|
+
private lastProbe;
|
|
10
|
+
constructor(options: SOCWardenOptions);
|
|
11
|
+
/**
|
|
12
|
+
* Track a security event using named arguments.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* await soc.track('auth.login.success', { actor: user.id });
|
|
16
|
+
* await soc.track('data.exported', {
|
|
17
|
+
* actor: { id: user.id, email: user.email },
|
|
18
|
+
* metadata: { format: 'csv' },
|
|
19
|
+
* resource: { type: 'Report', id: report.id },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
track(event: string, options?: TrackOptions): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Track a security event using a raw data object.
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* await soc.trackData('auth.login.success', {
|
|
29
|
+
* actor_id: user.id,
|
|
30
|
+
* actor_email: user.email,
|
|
31
|
+
* metadata: { role: 'admin' },
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
trackData(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Start building an event with the fluent API.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* await soc.event('data.exported')
|
|
41
|
+
* .actor(user.id)
|
|
42
|
+
* .resource('Report', report.id)
|
|
43
|
+
* .meta('format', 'csv')
|
|
44
|
+
* .send();
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
event(name: string): EventBuilder;
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated No-op — context is now stored per-request in AsyncLocalStorage.
|
|
50
|
+
* Kept for API compatibility only; will be removed in a future major version.
|
|
51
|
+
*/
|
|
52
|
+
setContext(): void;
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated No-op — context is now stored per-request in AsyncLocalStorage.
|
|
55
|
+
* Kept for API compatibility only; will be removed in a future major version.
|
|
56
|
+
*/
|
|
57
|
+
clearContext(): void;
|
|
58
|
+
private resolveNamedArgs;
|
|
59
|
+
private static readonly EVENT_TYPE_REGEX;
|
|
60
|
+
private dispatch;
|
|
61
|
+
private buildPayload;
|
|
62
|
+
private collectContext;
|
|
63
|
+
private sanitizeQueryString;
|
|
64
|
+
/**
|
|
65
|
+
* Send an event payload to the ingestor with 429 backoff handling.
|
|
66
|
+
*
|
|
67
|
+
* Backoff strategy (mirrors Laravel SDK):
|
|
68
|
+
* - On 429: back off for Retry-After seconds (default 1 hour).
|
|
69
|
+
* - During backoff: silently drop events, except for a probe every 5 minutes.
|
|
70
|
+
* - On successful probe: clear backoff and resume normal sending.
|
|
71
|
+
*/
|
|
72
|
+
private send;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAGL,gBAAgB,EAChB,YAAY,EACb,MAAM,SAAS,CAAC;AA6BjB,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,gDAAgD;IAChD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,SAAS,CAAa;gBAKlB,OAAO,EAAE,gBAAgB;IAsBrC;;;;;;;;;;;OAWG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE;;;;;;;;;;OAUG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjF;;;;;;;;;;OAUG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IAIjC;;;OAGG;IACH,UAAU,IAAI,IAAI;IAKlB;;;OAGG;IACH,YAAY,IAAI,IAAI;IAQpB,OAAO,CAAC,gBAAgB;IAgExB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAwD;YAElF,QAAQ;IAiBtB,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,cAAc;IAuDtB,OAAO,CAAC,mBAAmB;IAiB3B;;;;;;;OAOG;YACW,IAAI;CAqDnB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SOCWardenClient = void 0;
|
|
4
|
+
const builder_1 = require("./builder");
|
|
5
|
+
const os_1 = require("os");
|
|
6
|
+
const context_1 = require("./context");
|
|
7
|
+
const SDK_NAME = 'socwarden-node';
|
|
8
|
+
const SDK_VERSION = '1.0.0';
|
|
9
|
+
const BACKOFF_DURATION = 3600; // 1 hour in seconds
|
|
10
|
+
const PROBE_INTERVAL = 300; // 5 minutes in seconds
|
|
11
|
+
const SENSITIVE_PARAMS = ['token', 'key', 'password', 'secret', 'code', 'auth', 'session', 'csrf'];
|
|
12
|
+
/**
|
|
13
|
+
* Returns ip if it is a valid IPv4 or IPv6 address, otherwise undefined.
|
|
14
|
+
* Matches the ingestor's validate:"omitempty,ip" constraint.
|
|
15
|
+
*/
|
|
16
|
+
function sanitizeIP(ip) {
|
|
17
|
+
if (!ip)
|
|
18
|
+
return undefined;
|
|
19
|
+
// IPv4
|
|
20
|
+
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
21
|
+
if (ipv4.test(ip)) {
|
|
22
|
+
const parts = ip.split('.').map(Number);
|
|
23
|
+
if (parts.every((p) => p >= 0 && p <= 255))
|
|
24
|
+
return ip;
|
|
25
|
+
}
|
|
26
|
+
// IPv6: contains colon and only hex digits and colons
|
|
27
|
+
if (ip.includes(':') && /^[0-9a-fA-F:]+$/.test(ip))
|
|
28
|
+
return ip;
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
class SOCWardenClient {
|
|
32
|
+
apiKey;
|
|
33
|
+
endpoint;
|
|
34
|
+
timeout;
|
|
35
|
+
/** In-memory backoff state for 429 handling. */
|
|
36
|
+
backoffUntil = 0;
|
|
37
|
+
lastProbe = 0;
|
|
38
|
+
// D4 FIX: Per-request context is now stored in AsyncLocalStorage (see middleware.ts)
|
|
39
|
+
// rather than on the shared instance, preventing concurrent request contamination.
|
|
40
|
+
constructor(options) {
|
|
41
|
+
if (!options.apiKey) {
|
|
42
|
+
throw new Error('[SOCWarden] apiKey is required');
|
|
43
|
+
}
|
|
44
|
+
this.apiKey = options.apiKey;
|
|
45
|
+
this.endpoint = (options.endpoint ?? 'https://ingest.socwarden.io').replace(/\/+$/, '');
|
|
46
|
+
this.timeout = options.timeout ?? 5000;
|
|
47
|
+
// D2 FIX: Enforce HTTPS to prevent API key transmission in cleartext.
|
|
48
|
+
if (!this.endpoint.startsWith('https://')) {
|
|
49
|
+
if (process.env.NODE_ENV === 'production') {
|
|
50
|
+
throw new Error('[SOCWarden] Endpoint must use HTTPS in production. API keys must not be transmitted in cleartext.');
|
|
51
|
+
}
|
|
52
|
+
console.warn('[SOCWarden] WARNING: Endpoint is using HTTP. API keys will be transmitted in cleartext.');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Public API
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
/**
|
|
59
|
+
* Track a security event using named arguments.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* await soc.track('auth.login.success', { actor: user.id });
|
|
63
|
+
* await soc.track('data.exported', {
|
|
64
|
+
* actor: { id: user.id, email: user.email },
|
|
65
|
+
* metadata: { format: 'csv' },
|
|
66
|
+
* resource: { type: 'Report', id: report.id },
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
async track(event, options) {
|
|
71
|
+
const data = options ? this.resolveNamedArgs(options) : {};
|
|
72
|
+
await this.dispatch(event, data);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Track a security event using a raw data object.
|
|
76
|
+
*
|
|
77
|
+
* ```ts
|
|
78
|
+
* await soc.trackData('auth.login.success', {
|
|
79
|
+
* actor_id: user.id,
|
|
80
|
+
* actor_email: user.email,
|
|
81
|
+
* metadata: { role: 'admin' },
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
async trackData(event, data = {}) {
|
|
86
|
+
await this.dispatch(event, data);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Start building an event with the fluent API.
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* await soc.event('data.exported')
|
|
93
|
+
* .actor(user.id)
|
|
94
|
+
* .resource('Report', report.id)
|
|
95
|
+
* .meta('format', 'csv')
|
|
96
|
+
* .send();
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
event(name) {
|
|
100
|
+
return new builder_1.EventBuilder(name, this);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @deprecated No-op — context is now stored per-request in AsyncLocalStorage.
|
|
104
|
+
* Kept for API compatibility only; will be removed in a future major version.
|
|
105
|
+
*/
|
|
106
|
+
setContext() {
|
|
107
|
+
// D4 FIX: Context is stored in AsyncLocalStorage by the middleware.
|
|
108
|
+
// This method is intentionally a no-op.
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated No-op — context is now stored per-request in AsyncLocalStorage.
|
|
112
|
+
* Kept for API compatibility only; will be removed in a future major version.
|
|
113
|
+
*/
|
|
114
|
+
clearContext() {
|
|
115
|
+
// D4 FIX: AsyncLocalStorage context is automatically scoped to the request.
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Internal: argument resolution
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
resolveNamedArgs(options) {
|
|
121
|
+
const data = {};
|
|
122
|
+
// Actor: object reads id + email; string is just id
|
|
123
|
+
if (options.actor !== undefined) {
|
|
124
|
+
if (typeof options.actor === 'string') {
|
|
125
|
+
data.actor_id = options.actor;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
data.actor_id = options.actor.id;
|
|
129
|
+
if (options.actor.email) {
|
|
130
|
+
data.actor_email = options.actor.email;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Explicit scalars override actor-resolved values
|
|
135
|
+
if (options.actorId !== undefined) {
|
|
136
|
+
data.actor_id = options.actorId;
|
|
137
|
+
}
|
|
138
|
+
if (options.actorEmail !== undefined) {
|
|
139
|
+
data.actor_email = options.actorEmail;
|
|
140
|
+
}
|
|
141
|
+
if (options.ip !== undefined) {
|
|
142
|
+
data.ip = sanitizeIP(options.ip);
|
|
143
|
+
}
|
|
144
|
+
if (options.userAgent !== undefined) {
|
|
145
|
+
data.user_agent = options.userAgent;
|
|
146
|
+
}
|
|
147
|
+
if (options.metadata !== undefined) {
|
|
148
|
+
data.metadata = { ...options.metadata };
|
|
149
|
+
}
|
|
150
|
+
if (options.timestamp !== undefined) {
|
|
151
|
+
data.timestamp =
|
|
152
|
+
options.timestamp instanceof Date
|
|
153
|
+
? options.timestamp.toISOString()
|
|
154
|
+
: options.timestamp;
|
|
155
|
+
}
|
|
156
|
+
// Resource: object reads type + id; string is just type
|
|
157
|
+
if (options.resource !== undefined) {
|
|
158
|
+
const meta = (data.metadata ?? {});
|
|
159
|
+
if (typeof options.resource === 'string') {
|
|
160
|
+
meta.resource_type = options.resource;
|
|
161
|
+
if (options.resourceId !== undefined) {
|
|
162
|
+
meta.resource_id = options.resourceId;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
meta.resource_type = options.resource.type;
|
|
167
|
+
meta.resource_id = options.resource.id;
|
|
168
|
+
}
|
|
169
|
+
data.metadata = meta;
|
|
170
|
+
}
|
|
171
|
+
// Remove undefined values
|
|
172
|
+
return Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined && v !== null));
|
|
173
|
+
}
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Internal: dispatch and send
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// D3 FIX: Validate event_type format before sending to the ingestor.
|
|
178
|
+
static EVENT_TYPE_REGEX = /^[a-z][a-z0-9]{0,29}(\.[a-z][a-z0-9_]{0,29}){1,3}$/;
|
|
179
|
+
async dispatch(event, data) {
|
|
180
|
+
// D3 FIX: Validate event type format before sending.
|
|
181
|
+
if (!SOCWardenClient.EVENT_TYPE_REGEX.test(event)) {
|
|
182
|
+
console.warn(`[SOCWarden] Invalid event type format, dropping event: "${event}". ` +
|
|
183
|
+
'Event types must match ^[a-z][a-z0-9]{0,29}(\\.[a-z][a-z0-9_]{0,29}){1,3}$');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const payload = this.buildPayload(event, data);
|
|
187
|
+
try {
|
|
188
|
+
await this.send(payload);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
192
|
+
console.warn(`[SOCWarden] Failed to send event: ${message}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
buildPayload(event, data) {
|
|
196
|
+
const payload = {
|
|
197
|
+
event,
|
|
198
|
+
source: 'sdk',
|
|
199
|
+
};
|
|
200
|
+
const fields = ['actor_id', 'actor_email', 'user_agent', 'metadata', 'timestamp'];
|
|
201
|
+
for (const field of fields) {
|
|
202
|
+
if (data[field] !== undefined) {
|
|
203
|
+
payload[field] = data[field];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (data.ip !== undefined) {
|
|
207
|
+
payload.ip = data.ip;
|
|
208
|
+
}
|
|
209
|
+
// Attach auto-context if middleware captured it
|
|
210
|
+
payload.context = this.collectContext(data);
|
|
211
|
+
return payload;
|
|
212
|
+
}
|
|
213
|
+
collectContext(data) {
|
|
214
|
+
const context = {
|
|
215
|
+
sdk: {
|
|
216
|
+
name: SDK_NAME,
|
|
217
|
+
version: SDK_VERSION,
|
|
218
|
+
},
|
|
219
|
+
server: {
|
|
220
|
+
hostname: (0, os_1.hostname)(),
|
|
221
|
+
runtime: `Node.js ${process.version}`,
|
|
222
|
+
pid: process.pid,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
// D4 FIX: Read per-request context from AsyncLocalStorage instead of the
|
|
226
|
+
// shared instance property to prevent concurrent request contamination.
|
|
227
|
+
const requestCtx = context_1.requestContextStorage.getStore();
|
|
228
|
+
if (requestCtx?.request) {
|
|
229
|
+
const req = requestCtx.request;
|
|
230
|
+
context.request = {
|
|
231
|
+
method: req.method,
|
|
232
|
+
path: req.path,
|
|
233
|
+
};
|
|
234
|
+
if (req.ip) {
|
|
235
|
+
context.request.ip = req.ip;
|
|
236
|
+
}
|
|
237
|
+
if (req.queryString) {
|
|
238
|
+
context.request.query_string = this.sanitizeQueryString(req.queryString);
|
|
239
|
+
}
|
|
240
|
+
if (req.referer) {
|
|
241
|
+
context.request.referer = req.referer;
|
|
242
|
+
}
|
|
243
|
+
if (req.origin) {
|
|
244
|
+
context.request.origin = req.origin;
|
|
245
|
+
}
|
|
246
|
+
if (req.contentType) {
|
|
247
|
+
context.request.content_type = req.contentType;
|
|
248
|
+
}
|
|
249
|
+
if (req.acceptLanguage) {
|
|
250
|
+
context.request.accept_language = req.acceptLanguage;
|
|
251
|
+
}
|
|
252
|
+
if (req.requestId) {
|
|
253
|
+
context.request.request_id = req.requestId;
|
|
254
|
+
}
|
|
255
|
+
if (req.userAgent) {
|
|
256
|
+
context.request.user_agent = req.userAgent;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// D1 FIX: Browser context from X-SOCWarden-Context header removed —
|
|
260
|
+
// trusting arbitrary HTTP headers allows spoofing of server-side metadata.
|
|
261
|
+
return context;
|
|
262
|
+
}
|
|
263
|
+
sanitizeQueryString(qs) {
|
|
264
|
+
if (!qs)
|
|
265
|
+
return '';
|
|
266
|
+
return qs
|
|
267
|
+
.split('&')
|
|
268
|
+
.map((pair) => {
|
|
269
|
+
const [key, ...rest] = pair.split('=');
|
|
270
|
+
const paramName = key.toLowerCase();
|
|
271
|
+
const isSensitive = SENSITIVE_PARAMS.some((s) => paramName.includes(s));
|
|
272
|
+
if (isSensitive && rest.length > 0) {
|
|
273
|
+
return `${key}=[REDACTED]`;
|
|
274
|
+
}
|
|
275
|
+
return pair;
|
|
276
|
+
})
|
|
277
|
+
.join('&');
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Send an event payload to the ingestor with 429 backoff handling.
|
|
281
|
+
*
|
|
282
|
+
* Backoff strategy (mirrors Laravel SDK):
|
|
283
|
+
* - On 429: back off for Retry-After seconds (default 1 hour).
|
|
284
|
+
* - During backoff: silently drop events, except for a probe every 5 minutes.
|
|
285
|
+
* - On successful probe: clear backoff and resume normal sending.
|
|
286
|
+
*/
|
|
287
|
+
async send(payload) {
|
|
288
|
+
const now = Math.floor(Date.now() / 1000);
|
|
289
|
+
// Check backoff
|
|
290
|
+
if (this.backoffUntil > 0 && now < this.backoffUntil) {
|
|
291
|
+
// During backoff, only send probes at PROBE_INTERVAL
|
|
292
|
+
if (now - this.lastProbe < PROBE_INTERVAL) {
|
|
293
|
+
return; // silently drop
|
|
294
|
+
}
|
|
295
|
+
this.lastProbe = now;
|
|
296
|
+
}
|
|
297
|
+
const controller = new AbortController();
|
|
298
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
299
|
+
try {
|
|
300
|
+
const response = await fetch(`${this.endpoint}/v1/events`, {
|
|
301
|
+
method: 'POST',
|
|
302
|
+
headers: {
|
|
303
|
+
'Content-Type': 'application/json',
|
|
304
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
305
|
+
},
|
|
306
|
+
body: JSON.stringify(payload),
|
|
307
|
+
signal: controller.signal,
|
|
308
|
+
});
|
|
309
|
+
if (response.status === 429) {
|
|
310
|
+
const retryAfter = parseInt(response.headers.get('Retry-After') ?? '', 10);
|
|
311
|
+
const backoffSeconds = isNaN(retryAfter) ? BACKOFF_DURATION : retryAfter;
|
|
312
|
+
this.backoffUntil = now + backoffSeconds;
|
|
313
|
+
console.warn(`[SOCWarden] Quota exceeded (429). Backing off for ${backoffSeconds}s`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// Clear backoff on any successful response
|
|
317
|
+
if (response.ok && this.backoffUntil > 0) {
|
|
318
|
+
this.backoffUntil = 0;
|
|
319
|
+
this.lastProbe = 0;
|
|
320
|
+
console.info('[SOCWarden] Quota restored, backoff cleared');
|
|
321
|
+
}
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
const body = await response.text().catch(() => '');
|
|
324
|
+
console.warn(`[SOCWarden] Event send failed (HTTP ${response.status}): ${body}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
clearTimeout(timeoutId);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
exports.SOCWardenClient = SOCWardenClient;
|
|
333
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
import type { CapturedContext } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* D4 FIX: Per-request AsyncLocalStorage context store.
|
|
5
|
+
*
|
|
6
|
+
* Storing per-request context (IP, user-agent, etc.) on the shared singleton
|
|
7
|
+
* SOCWardenClient instance causes concurrent requests to contaminate each
|
|
8
|
+
* other's context. AsyncLocalStorage provides automatic isolation: each
|
|
9
|
+
* request runs in its own async context and getStore() always returns the
|
|
10
|
+
* correct context for the current request, even under high concurrency.
|
|
11
|
+
*/
|
|
12
|
+
export declare const requestContextStorage: AsyncLocalStorage<CapturedContext>;
|
|
13
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,oCAA2C,CAAC"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestContextStorage = void 0;
|
|
4
|
+
const async_hooks_1 = require("async_hooks");
|
|
5
|
+
/**
|
|
6
|
+
* D4 FIX: Per-request AsyncLocalStorage context store.
|
|
7
|
+
*
|
|
8
|
+
* Storing per-request context (IP, user-agent, etc.) on the shared singleton
|
|
9
|
+
* SOCWardenClient instance causes concurrent requests to contaminate each
|
|
10
|
+
* other's context. AsyncLocalStorage provides automatic isolation: each
|
|
11
|
+
* request runs in its own async context and getStore() always returns the
|
|
12
|
+
* correct context for the current request, even under high concurrency.
|
|
13
|
+
*/
|
|
14
|
+
exports.requestContextStorage = new async_hooks_1.AsyncLocalStorage();
|
|
15
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":";;;AAAA,6CAAgD;AAGhD;;;;;;;;GAQG;AACU,QAAA,qBAAqB,GAAG,IAAI,+BAAiB,EAAmB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SOCWardenClient } from './client';
|
|
2
|
+
export { EventBuilder } from './builder';
|
|
3
|
+
export { socwardenMiddleware } from './middleware';
|
|
4
|
+
export type { SOCWardenOptions, TrackOptions, ActorInput, ResourceInput, EventPayload, RequestContext, CapturedContext, } from './types';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,eAAe,GAChB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.socwardenMiddleware = exports.EventBuilder = exports.SOCWardenClient = void 0;
|
|
4
|
+
var client_1 = require("./client");
|
|
5
|
+
Object.defineProperty(exports, "SOCWardenClient", { enumerable: true, get: function () { return client_1.SOCWardenClient; } });
|
|
6
|
+
var builder_1 = require("./builder");
|
|
7
|
+
Object.defineProperty(exports, "EventBuilder", { enumerable: true, get: function () { return builder_1.EventBuilder; } });
|
|
8
|
+
var middleware_1 = require("./middleware");
|
|
9
|
+
Object.defineProperty(exports, "socwardenMiddleware", { enumerable: true, get: function () { return middleware_1.socwardenMiddleware; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAA2C;AAAlC,yGAAA,eAAe,OAAA;AACxB,qCAAyC;AAAhC,uGAAA,YAAY,OAAA;AACrB,2CAAmD;AAA1C,iHAAA,mBAAmB,OAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SOCWardenClient } from './client';
|
|
2
|
+
export { requestContextStorage } from './context';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal Express-compatible request interface.
|
|
5
|
+
* Avoids requiring Express as a dependency.
|
|
6
|
+
*/
|
|
7
|
+
interface ExpressRequest {
|
|
8
|
+
method: string;
|
|
9
|
+
path: string;
|
|
10
|
+
ip?: string;
|
|
11
|
+
query?: Record<string, unknown>;
|
|
12
|
+
get(header: string): string | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Minimal Express-compatible response interface.
|
|
16
|
+
*/
|
|
17
|
+
interface ExpressResponse {
|
|
18
|
+
on(event: string, listener: () => void): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Minimal Express-compatible next function.
|
|
22
|
+
*/
|
|
23
|
+
type NextFunction = (err?: unknown) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Express middleware that captures request context for SOCWarden events.
|
|
26
|
+
*
|
|
27
|
+
* When active, every event sent during the request lifecycle will automatically
|
|
28
|
+
* include request metadata (method, path, IP, user-agent, etc.) in the
|
|
29
|
+
* `context` field of the payload.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* import express from 'express';
|
|
33
|
+
* import { SOCWardenClient, socwardenMiddleware } from '@socwarden/sdk';
|
|
34
|
+
*
|
|
35
|
+
* const soc = new SOCWardenClient({ apiKey: 'sk_live_...' });
|
|
36
|
+
* const app = express();
|
|
37
|
+
*
|
|
38
|
+
* app.use(socwardenMiddleware(soc));
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function socwardenMiddleware(client: SOCWardenClient): (req: ExpressRequest, _res: ExpressResponse, next: NextFunction) => void;
|
|
42
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAElD;;;GAGG;AACH,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAC/C;AAED;;GAEG;AACH,KAAK,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,IAMjD,KAAK,cAAc,EAAE,MAAM,eAAe,EAAE,MAAM,YAAY,KAAG,IAAI,CA8B9E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestContextStorage = void 0;
|
|
4
|
+
exports.socwardenMiddleware = socwardenMiddleware;
|
|
5
|
+
const context_1 = require("./context");
|
|
6
|
+
var context_2 = require("./context");
|
|
7
|
+
Object.defineProperty(exports, "requestContextStorage", { enumerable: true, get: function () { return context_2.requestContextStorage; } });
|
|
8
|
+
/**
|
|
9
|
+
* Express middleware that captures request context for SOCWarden events.
|
|
10
|
+
*
|
|
11
|
+
* When active, every event sent during the request lifecycle will automatically
|
|
12
|
+
* include request metadata (method, path, IP, user-agent, etc.) in the
|
|
13
|
+
* `context` field of the payload.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import express from 'express';
|
|
17
|
+
* import { SOCWardenClient, socwardenMiddleware } from '@socwarden/sdk';
|
|
18
|
+
*
|
|
19
|
+
* const soc = new SOCWardenClient({ apiKey: 'sk_live_...' });
|
|
20
|
+
* const app = express();
|
|
21
|
+
*
|
|
22
|
+
* app.use(socwardenMiddleware(soc));
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function socwardenMiddleware(client) {
|
|
26
|
+
// D4 FIX: client parameter retained for API compatibility but context is now
|
|
27
|
+
// stored in AsyncLocalStorage instead of on the shared singleton instance,
|
|
28
|
+
// preventing concurrent request context contamination.
|
|
29
|
+
void client;
|
|
30
|
+
return (req, _res, next) => {
|
|
31
|
+
const queryString = buildQueryString(req.query);
|
|
32
|
+
const capturedContext = {
|
|
33
|
+
request: {
|
|
34
|
+
method: req.method,
|
|
35
|
+
path: req.path,
|
|
36
|
+
ip: req.ip ?? req.get('x-forwarded-for')?.split(',')[0].trim(),
|
|
37
|
+
userAgent: req.get('user-agent'),
|
|
38
|
+
queryString,
|
|
39
|
+
referer: req.get('referer'),
|
|
40
|
+
origin: req.get('origin'),
|
|
41
|
+
contentType: req.get('content-type'),
|
|
42
|
+
acceptLanguage: req.get('accept-language'),
|
|
43
|
+
requestId: req.get('x-request-id') ?? req.get('x-correlation-id'),
|
|
44
|
+
},
|
|
45
|
+
sdk: {
|
|
46
|
+
name: 'socwarden-node',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
// D1 FIX: X-SOCWarden-Context header removed — trusting arbitrary HTTP headers
|
|
51
|
+
// allows any client to spoof server-side metadata. Server context is collected
|
|
52
|
+
// locally by the SDK and must not be merged from incoming request headers.
|
|
53
|
+
// D4 FIX: Run the rest of the request handling within the AsyncLocalStorage
|
|
54
|
+
// context so each concurrent request has its own isolated context.
|
|
55
|
+
context_1.requestContextStorage.run(capturedContext, next);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reconstruct a query string from Express's parsed query object.
|
|
60
|
+
*/
|
|
61
|
+
function buildQueryString(query) {
|
|
62
|
+
if (!query)
|
|
63
|
+
return undefined;
|
|
64
|
+
const entries = Object.entries(query).filter(([, v]) => v !== undefined && v !== null);
|
|
65
|
+
if (entries.length === 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
return entries
|
|
68
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
|
|
69
|
+
.join('&');
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":";;;AA8CA,kDAoCC;AAjFD,uCAAkD;AAElD,qCAAkD;AAAzC,gHAAA,qBAAqB,OAAA;AA0B9B;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,mBAAmB,CAAC,MAAuB;IACzD,6EAA6E;IAC7E,2EAA2E;IAC3E,uDAAuD;IACvD,KAAK,MAAM,CAAC;IAEZ,OAAO,CAAC,GAAmB,EAAE,IAAqB,EAAE,IAAkB,EAAQ,EAAE;QAC9E,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,eAAe,GAAoB;YACvC,OAAO,EAAE;gBACP,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBAC9D,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;gBAChC,WAAW;gBACX,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;gBAC3B,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACzB,WAAW,EAAE,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;gBACpC,cAAc,EAAE,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC;gBAC1C,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC;aAClE;YACD,GAAG,EAAE;gBACH,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,OAAO;aACjB;SACF,CAAC;QAEF,+EAA+E;QAC/E,+EAA+E;QAC/E,2EAA2E;QAE3E,4EAA4E;QAC5E,mEAAmE;QACnE,+BAAqB,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,KAA+B;IACvD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAE7B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAC1C,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,CACzC,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3C,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5E,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the SOCWarden client.
|
|
3
|
+
*/
|
|
4
|
+
export interface SOCWardenOptions {
|
|
5
|
+
/** API key for authenticating with the SOCWarden ingestor. */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** Ingestor endpoint URL. Defaults to https://ingest.socwarden.io */
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
/** HTTP request timeout in milliseconds. Defaults to 5000. */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Actor identification — either a string ID or an object with id and optional email.
|
|
14
|
+
*/
|
|
15
|
+
export type ActorInput = string | {
|
|
16
|
+
id: string;
|
|
17
|
+
email?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resource identification — either a string type or an object with type and id.
|
|
21
|
+
*/
|
|
22
|
+
export type ResourceInput = string | {
|
|
23
|
+
type: string;
|
|
24
|
+
id: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Options for the `track()` method (named-args style).
|
|
28
|
+
*/
|
|
29
|
+
export interface TrackOptions {
|
|
30
|
+
/** Actor (user) who triggered the event. String is treated as actor ID. */
|
|
31
|
+
actor?: ActorInput;
|
|
32
|
+
/** Explicit actor ID (overrides actor if both provided). */
|
|
33
|
+
actorId?: string;
|
|
34
|
+
/** Explicit actor email (overrides actor.email if both provided). */
|
|
35
|
+
actorEmail?: string;
|
|
36
|
+
/** Source IP address. Auto-detected from request context if middleware is active. */
|
|
37
|
+
ip?: string;
|
|
38
|
+
/** User-Agent string. Auto-detected from request context if middleware is active. */
|
|
39
|
+
userAgent?: string;
|
|
40
|
+
/** Custom metadata key-value pairs. */
|
|
41
|
+
metadata?: Record<string, unknown>;
|
|
42
|
+
/** Event timestamp (ISO 8601 string or Date object). Defaults to now. */
|
|
43
|
+
timestamp?: string | Date;
|
|
44
|
+
/** Resource that was acted upon. String is treated as resource type. */
|
|
45
|
+
resource?: ResourceInput;
|
|
46
|
+
/** Explicit resource ID (used when resource is a string type). */
|
|
47
|
+
resourceId?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Payload sent to the SOCWarden ingestor POST /v1/events endpoint.
|
|
51
|
+
*/
|
|
52
|
+
export interface EventPayload {
|
|
53
|
+
event: string;
|
|
54
|
+
source: string;
|
|
55
|
+
actor_id?: string;
|
|
56
|
+
actor_email?: string;
|
|
57
|
+
ip?: string;
|
|
58
|
+
user_agent?: string;
|
|
59
|
+
metadata?: Record<string, unknown>;
|
|
60
|
+
timestamp?: string;
|
|
61
|
+
context?: RequestContext;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Auto-collected context attached to every event when middleware is active.
|
|
65
|
+
*/
|
|
66
|
+
export interface RequestContext {
|
|
67
|
+
sdk: {
|
|
68
|
+
name: string;
|
|
69
|
+
version: string;
|
|
70
|
+
};
|
|
71
|
+
server?: {
|
|
72
|
+
hostname: string;
|
|
73
|
+
runtime: string;
|
|
74
|
+
pid: number;
|
|
75
|
+
};
|
|
76
|
+
request?: {
|
|
77
|
+
method: string;
|
|
78
|
+
path: string;
|
|
79
|
+
ip?: string;
|
|
80
|
+
user_agent?: string;
|
|
81
|
+
query_string?: string;
|
|
82
|
+
referer?: string;
|
|
83
|
+
origin?: string;
|
|
84
|
+
content_type?: string;
|
|
85
|
+
accept_language?: string;
|
|
86
|
+
request_id?: string;
|
|
87
|
+
};
|
|
88
|
+
/** Browser context relayed from the browser SDK via X-SOCWarden-Context header. */
|
|
89
|
+
browser?: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Internal context captured by the Express middleware for the current request.
|
|
93
|
+
*/
|
|
94
|
+
export interface CapturedContext {
|
|
95
|
+
request: {
|
|
96
|
+
method: string;
|
|
97
|
+
path: string;
|
|
98
|
+
ip?: string;
|
|
99
|
+
userAgent?: string;
|
|
100
|
+
queryString?: string;
|
|
101
|
+
referer?: string;
|
|
102
|
+
origin?: string;
|
|
103
|
+
contentType?: string;
|
|
104
|
+
acceptLanguage?: string;
|
|
105
|
+
requestId?: string;
|
|
106
|
+
};
|
|
107
|
+
sdk: {
|
|
108
|
+
name: string;
|
|
109
|
+
version: string;
|
|
110
|
+
};
|
|
111
|
+
/** Browser context decoded from X-SOCWarden-Context header (relay mode). */
|
|
112
|
+
browser?: Record<string, unknown>;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@socwarden/sdk",
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
|
+
"description": "SOCWarden Node.js/TypeScript SDK for security event tracking",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "npx tsx --test src/__tests__/*.test.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"socwarden",
|
|
17
|
+
"security",
|
|
18
|
+
"soc",
|
|
19
|
+
"event-tracking",
|
|
20
|
+
"threat-detection"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^25.5.0",
|
|
25
|
+
"tsx": "^4.21.0",
|
|
26
|
+
"typescript": "^5.5.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|