@the_ro_show/agent-ads-sdk 0.1.1 → 0.2.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 +95 -0
- package/SECURITY.md +331 -5
- package/dist/index.d.mts +77 -1
- package/dist/index.d.ts +77 -1
- package/dist/index.js +118 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +115 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,63 @@ if (unit && unit.unit_type === 'sponsored_suggestion') {
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Security Best Practices
|
|
59
|
+
|
|
60
|
+
🔴 **CRITICAL SECURITY REQUIREMENTS**
|
|
61
|
+
|
|
62
|
+
### 1. Server-Side Only
|
|
63
|
+
|
|
64
|
+
**This SDK MUST only be used server-side.** Your API key provides full access to your account and billing.
|
|
65
|
+
|
|
66
|
+
✅ **Safe:** Node.js, serverless functions, server-side rendering
|
|
67
|
+
❌ **Unsafe:** Browser JavaScript, mobile apps without backend proxy
|
|
68
|
+
|
|
69
|
+
### 2. Sanitize Ad Content Before Display
|
|
70
|
+
|
|
71
|
+
Ad content can contain malicious HTML/JavaScript. **Always sanitize** before rendering in HTML:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { escapeHTML, sanitizeURL } from '@the_ro_show/agent-ads-sdk';
|
|
75
|
+
|
|
76
|
+
// ✅ SAFE: Sanitize content
|
|
77
|
+
const safeTitle = escapeHTML(unit.suggestion.title);
|
|
78
|
+
const safeBody = escapeHTML(unit.suggestion.body);
|
|
79
|
+
const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
80
|
+
|
|
81
|
+
if (safeURL) {
|
|
82
|
+
element.innerHTML = `<a href="${safeURL}">${safeTitle}</a>`;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// ❌ DANGEROUS: Direct HTML injection (XSS vulnerability!)
|
|
88
|
+
element.innerHTML = unit.suggestion.title;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 3. Validate URLs
|
|
92
|
+
|
|
93
|
+
Always validate `action_url` before using:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
97
|
+
|
|
98
|
+
if (!safeURL) {
|
|
99
|
+
console.error('Dangerous URL blocked');
|
|
100
|
+
return; // Don't render the ad
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Blocks dangerous protocols: `javascript:`, `data:`, `file:`
|
|
105
|
+
|
|
106
|
+
### 4. See Complete Guidelines
|
|
107
|
+
|
|
108
|
+
📖 **[Read SECURITY.md](./SECURITY.md)** for comprehensive security guidelines including:
|
|
109
|
+
- XSS prevention examples
|
|
110
|
+
- Phishing protection
|
|
111
|
+
- Rate limiting
|
|
112
|
+
- Input validation
|
|
113
|
+
- Security checklist
|
|
114
|
+
|
|
58
115
|
## Testing Without Advertiser Data
|
|
59
116
|
|
|
60
117
|
Use `MockAttentionMarketClient` to test your integration without needing real ad campaigns.
|
|
@@ -148,10 +205,12 @@ For production integrations:
|
|
|
148
205
|
- **[Claude (Anthropic)](./examples/claude-tool-use-full.ts)** - Complete tool use pattern with schemas and tracking
|
|
149
206
|
- **[OpenAI GPT](./examples/openai-function-calling-full.ts)** - Complete function calling with integration checklist
|
|
150
207
|
- **[Google Gemini](./examples/gemini-function-calling-full.ts)** - Complete function declarations with testing guide
|
|
208
|
+
- **[Safe Web Rendering](./examples/safe-web-rendering.ts)** - XSS prevention and secure HTML rendering
|
|
151
209
|
|
|
152
210
|
Run any example with:
|
|
153
211
|
```bash
|
|
154
212
|
npx tsx examples/claude-tool-use-minimal.ts
|
|
213
|
+
npx tsx examples/safe-web-rendering.ts
|
|
155
214
|
```
|
|
156
215
|
|
|
157
216
|
## Full Example with Raw Response
|
|
@@ -408,6 +467,42 @@ import { generateUUID } from '@the_ro_show/agent-ads-sdk';
|
|
|
408
467
|
const requestId = generateUUID();
|
|
409
468
|
```
|
|
410
469
|
|
|
470
|
+
### `escapeHTML(text): string`
|
|
471
|
+
|
|
472
|
+
Escape HTML special characters to prevent XSS attacks.
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
import { escapeHTML } from '@the_ro_show/agent-ads-sdk';
|
|
476
|
+
|
|
477
|
+
const safeTitle = escapeHTML(unit.suggestion.title);
|
|
478
|
+
element.innerHTML = safeTitle; // Safe from XSS
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Escapes: `&`, `<`, `>`, `"`, `'`, `/`
|
|
482
|
+
|
|
483
|
+
### `sanitizeURL(url, options?): string | null`
|
|
484
|
+
|
|
485
|
+
Validate and sanitize URLs to prevent XSS and phishing attacks.
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
import { sanitizeURL } from '@the_ro_show/agent-ads-sdk';
|
|
489
|
+
|
|
490
|
+
const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
491
|
+
|
|
492
|
+
if (safeURL) {
|
|
493
|
+
window.open(safeURL, '_blank');
|
|
494
|
+
} else {
|
|
495
|
+
console.error('Dangerous URL blocked');
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Blocked protocols:** `javascript:`, `data:`, `file:`, `vbscript:`
|
|
500
|
+
|
|
501
|
+
**Options:**
|
|
502
|
+
- `allowHttp: boolean` - Allow HTTP URLs (default: false, HTTPS only)
|
|
503
|
+
- `allowTel: boolean` - Allow tel: links (default: true)
|
|
504
|
+
- `allowMailto: boolean` - Allow mailto: links (default: true)
|
|
505
|
+
|
|
411
506
|
## Features
|
|
412
507
|
|
|
413
508
|
- ✅ TypeScript support with full type definitions
|
package/SECURITY.md
CHANGED
|
@@ -1,10 +1,71 @@
|
|
|
1
1
|
# Security Policy
|
|
2
2
|
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Server-Side Only Usage](#server-side-only-usage)
|
|
6
|
+
2. [API Key Management](#api-key-management)
|
|
7
|
+
3. [Cross-Site Scripting (XSS) Prevention](#cross-site-scripting-xss-prevention)
|
|
8
|
+
4. [Content Security & Phishing](#content-security--phishing)
|
|
9
|
+
5. [Input Validation](#input-validation)
|
|
10
|
+
6. [Rate Limiting & Abuse Prevention](#rate-limiting--abuse-prevention)
|
|
11
|
+
7. [Reporting Security Issues](#reporting-security-issues)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Server-Side Only Usage
|
|
16
|
+
|
|
17
|
+
🔴 **CRITICAL: This SDK MUST only be used server-side.**
|
|
18
|
+
|
|
19
|
+
Your API key provides full access to your account and billing. **Never use this SDK in client-side code** (browser, mobile apps, etc.).
|
|
20
|
+
|
|
21
|
+
### ✅ Safe Environments
|
|
22
|
+
|
|
23
|
+
- Node.js backend servers
|
|
24
|
+
- Serverless functions (AWS Lambda, Vercel Functions, Cloudflare Workers)
|
|
25
|
+
- Server-side rendering (Next.js getServerSideProps, SvelteKit load)
|
|
26
|
+
|
|
27
|
+
### ❌ Unsafe Environments
|
|
28
|
+
|
|
29
|
+
- Browser/frontend JavaScript
|
|
30
|
+
- Mobile apps (React Native, Flutter without backend proxy)
|
|
31
|
+
- Electron apps (renderer process)
|
|
32
|
+
- Browser extensions
|
|
33
|
+
|
|
34
|
+
### Client-Side Apps
|
|
35
|
+
|
|
36
|
+
If you need to show ads in a client-side app:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// ✅ CORRECT: Backend API route
|
|
40
|
+
// /api/get-ad
|
|
41
|
+
export async function GET(request) {
|
|
42
|
+
const client = new AttentionMarketClient({
|
|
43
|
+
apiKey: process.env.ATTENTIONMARKET_API_KEY, // Server-side only
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const unit = await client.decide({...});
|
|
47
|
+
return Response.json(unit);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ✅ CORRECT: Frontend fetches from your backend
|
|
51
|
+
const response = await fetch('/api/get-ad');
|
|
52
|
+
const unit = await response.json();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// ❌ WRONG: API key exposed in browser
|
|
57
|
+
const client = new AttentionMarketClient({
|
|
58
|
+
apiKey: 'am_live_...', // EXPOSED TO USERS!
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
3
64
|
## API Key Management
|
|
4
65
|
|
|
5
66
|
**IMPORTANT**: Never commit API keys to version control.
|
|
6
67
|
|
|
7
|
-
Your AttentionMarket API key (`am_live_...` or `am_test_...`) provides access to your agent's account and billing.
|
|
68
|
+
Your AttentionMarket API key (`am_live_...` or `am_test_...`) provides access to your agent's account and billing.
|
|
8
69
|
|
|
9
70
|
### Environment Variables
|
|
10
71
|
|
|
@@ -12,6 +73,7 @@ Store your API key in environment variables, not in code:
|
|
|
12
73
|
|
|
13
74
|
```bash
|
|
14
75
|
export ATTENTIONMARKET_API_KEY=am_live_...
|
|
76
|
+
export ATTENTIONMARKET_AGENT_ID=agt_01HV...
|
|
15
77
|
```
|
|
16
78
|
|
|
17
79
|
Then use it in your application:
|
|
@@ -30,21 +92,285 @@ Ensure your `.gitignore` includes:
|
|
|
30
92
|
.env
|
|
31
93
|
.env.local
|
|
32
94
|
.env.*.local
|
|
95
|
+
*.pem
|
|
96
|
+
*.key
|
|
97
|
+
credentials.json
|
|
33
98
|
```
|
|
34
99
|
|
|
35
100
|
### Production Deployments
|
|
36
101
|
|
|
37
|
-
- Use secure secret management
|
|
38
|
-
- Rotate API keys periodically
|
|
102
|
+
- Use secure secret management (AWS Secrets Manager, GitHub Secrets, HashiCorp Vault)
|
|
103
|
+
- Rotate API keys periodically (at least every 90 days)
|
|
39
104
|
- Use `am_test_...` keys for development and testing
|
|
40
105
|
- Use `am_live_...` keys only in production environments
|
|
106
|
+
- Never log API keys (even partially masked)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Cross-Site Scripting (XSS) Prevention
|
|
111
|
+
|
|
112
|
+
🔴 **CRITICAL: Ad content can contain malicious HTML/JavaScript.**
|
|
113
|
+
|
|
114
|
+
Advertisers could inject malicious code into ad titles, bodies, or CTAs. You **MUST** sanitize all ad content before displaying it.
|
|
115
|
+
|
|
116
|
+
### The Risk
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// ❌ DANGEROUS: Direct HTML injection
|
|
120
|
+
const unit = await client.decide({...});
|
|
121
|
+
document.getElementById('ad').innerHTML = unit.suggestion.title;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
If `title` contains `<img src=x onerror=alert(document.cookie)>`, this executes JavaScript and steals cookies.
|
|
125
|
+
|
|
126
|
+
### Safe Rendering
|
|
127
|
+
|
|
128
|
+
**Option 1: Use SDK sanitization helpers** (Recommended)
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { escapeHTML, sanitizeURL } from '@the_ro_show/agent-ads-sdk';
|
|
132
|
+
|
|
133
|
+
if (unit && unit.unit_type === 'sponsored_suggestion') {
|
|
134
|
+
const safeTitle = escapeHTML(unit.suggestion.title);
|
|
135
|
+
const safeBody = escapeHTML(unit.suggestion.body);
|
|
136
|
+
const safeCTA = escapeHTML(unit.suggestion.cta);
|
|
137
|
+
const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
138
|
+
|
|
139
|
+
// Safe to inject
|
|
140
|
+
document.getElementById('ad-title').innerHTML = safeTitle;
|
|
141
|
+
document.getElementById('ad-body').innerHTML = safeBody;
|
|
142
|
+
|
|
143
|
+
// For links, also validate URL scheme
|
|
144
|
+
if (safeURL) {
|
|
145
|
+
document.getElementById('ad-link').href = safeURL;
|
|
146
|
+
document.getElementById('ad-link').textContent = safeCTA;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Option 2: Use textContent instead of innerHTML**
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// ✅ SAFE: Text-only rendering
|
|
155
|
+
document.getElementById('ad-title').textContent = unit.suggestion.title;
|
|
156
|
+
document.getElementById('ad-body').textContent = unit.suggestion.body;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Option 3: Use a sanitization library**
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import DOMPurify from 'dompurify';
|
|
163
|
+
|
|
164
|
+
const safeHTML = DOMPurify.sanitize(unit.suggestion.title);
|
|
165
|
+
document.getElementById('ad').innerHTML = safeHTML;
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### React/Vue/Angular
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// ✅ React (automatically escapes)
|
|
172
|
+
<div className="ad-title">{unit.suggestion.title}</div>
|
|
173
|
+
|
|
174
|
+
// ❌ React (dangerous)
|
|
175
|
+
<div dangerouslySetInnerHTML={{__html: unit.suggestion.title}} />
|
|
176
|
+
|
|
177
|
+
// ✅ Vue (automatically escapes)
|
|
178
|
+
<div>{{ unit.suggestion.title }}</div>
|
|
179
|
+
|
|
180
|
+
// ❌ Vue (dangerous)
|
|
181
|
+
<div v-html="unit.suggestion.title"></div>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Content Security & Phishing
|
|
187
|
+
|
|
188
|
+
Ad content may contain:
|
|
189
|
+
- Phishing links (`action_url: "http://paypa1.com/login"`)
|
|
190
|
+
- Malicious file downloads (`action_url: "javascript:downloadMalware()"`)
|
|
191
|
+
- Social engineering attacks
|
|
192
|
+
|
|
193
|
+
### URL Validation
|
|
194
|
+
|
|
195
|
+
Always validate `action_url` before using it:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { sanitizeURL } from '@the_ro_show/agent-ads-sdk';
|
|
199
|
+
|
|
200
|
+
const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
201
|
+
|
|
202
|
+
if (!safeURL) {
|
|
203
|
+
console.error('Invalid or dangerous URL:', unit.suggestion.action_url);
|
|
204
|
+
// Don't render the ad
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Safe to use
|
|
209
|
+
window.open(safeURL, '_blank');
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Allowed URL schemes:
|
|
213
|
+
- ✅ `https://`
|
|
214
|
+
- ✅ `http://` (with warning)
|
|
215
|
+
- ✅ `tel:` (phone numbers)
|
|
216
|
+
- ✅ `mailto:` (email)
|
|
217
|
+
- ❌ `javascript:` (blocked)
|
|
218
|
+
- ❌ `data:` (blocked)
|
|
219
|
+
- ❌ `file:` (blocked)
|
|
220
|
+
|
|
221
|
+
### Content Security Policy (CSP)
|
|
222
|
+
|
|
223
|
+
Add CSP headers to your web app:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Input Validation
|
|
232
|
+
|
|
233
|
+
### User Input Sanitization
|
|
234
|
+
|
|
235
|
+
If you pass user input to the SDK (e.g., search queries), sanitize it first:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// ✅ SAFE: Limit length and remove dangerous characters
|
|
239
|
+
function sanitizeQuery(userInput: string): string {
|
|
240
|
+
return userInput
|
|
241
|
+
.slice(0, 500) // Max length
|
|
242
|
+
.replace(/[<>'"]/g, ''); // Remove HTML chars
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const opportunity = createOpportunity({
|
|
246
|
+
taxonomy: 'local_services.movers.quote',
|
|
247
|
+
query: sanitizeQuery(userProvidedQuery),
|
|
248
|
+
// ...
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Taxonomy Validation
|
|
253
|
+
|
|
254
|
+
Only use known, trusted taxonomies:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const ALLOWED_TAXONOMIES = [
|
|
258
|
+
'local_services.movers.quote',
|
|
259
|
+
'local_services.restaurants.search',
|
|
260
|
+
// ...your list
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
function validateTaxonomy(taxonomy: string): boolean {
|
|
264
|
+
return ALLOWED_TAXONOMIES.includes(taxonomy);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ✅ Validate before use
|
|
268
|
+
if (!validateTaxonomy(userTaxonomy)) {
|
|
269
|
+
throw new Error('Invalid taxonomy');
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Rate Limiting & Abuse Prevention
|
|
276
|
+
|
|
277
|
+
### SDK-Side Limits
|
|
278
|
+
|
|
279
|
+
The SDK has **no built-in rate limiting**. You must implement this:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { RateLimiter } from 'limiter';
|
|
283
|
+
|
|
284
|
+
const limiter = new RateLimiter({
|
|
285
|
+
tokensPerInterval: 100,
|
|
286
|
+
interval: 'minute',
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
async function getAd() {
|
|
290
|
+
await limiter.removeTokens(1);
|
|
291
|
+
return client.decide({...});
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Prevent Tracking Abuse
|
|
296
|
+
|
|
297
|
+
Don't allow users to trigger tracking events directly:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// ❌ DANGEROUS: User controls tracking
|
|
301
|
+
app.post('/track-impression', async (req) => {
|
|
302
|
+
await client.trackImpression(req.body); // User can fake metrics!
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// ✅ SAFE: Server validates before tracking
|
|
306
|
+
app.post('/track-impression', async (req) => {
|
|
307
|
+
const { unitId } = req.body;
|
|
308
|
+
|
|
309
|
+
// Verify this unit was actually shown to this user
|
|
310
|
+
if (!isValidImpressionForUser(req.session.userId, unitId)) {
|
|
311
|
+
return res.status(403).json({ error: 'Invalid impression' });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
await client.trackImpression({...});
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Request Deduplication
|
|
319
|
+
|
|
320
|
+
Prevent duplicate tracking:
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
const impressionsSeen = new Set();
|
|
324
|
+
|
|
325
|
+
async function trackOnce(unitId: string) {
|
|
326
|
+
if (impressionsSeen.has(unitId)) {
|
|
327
|
+
return; // Already tracked
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
impressionsSeen.add(unitId);
|
|
331
|
+
await client.trackImpression({...});
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
41
336
|
|
|
42
337
|
## Reporting Security Issues
|
|
43
338
|
|
|
44
|
-
If you discover a security vulnerability in this SDK, please email
|
|
339
|
+
If you discover a security vulnerability in this SDK, please email:
|
|
340
|
+
|
|
341
|
+
**security@attentionmarket.com**
|
|
45
342
|
|
|
343
|
+
Include:
|
|
46
344
|
- Description of the vulnerability
|
|
47
345
|
- Steps to reproduce
|
|
48
346
|
- Potential impact
|
|
347
|
+
- Your contact information (for follow-up)
|
|
348
|
+
|
|
349
|
+
**Do not:**
|
|
350
|
+
- Open public GitHub issues for security vulnerabilities
|
|
351
|
+
- Post on social media or forums
|
|
352
|
+
- Exploit the vulnerability
|
|
353
|
+
|
|
354
|
+
We will acknowledge your report within 48 hours and provide updates on the fix timeline.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Security Checklist
|
|
359
|
+
|
|
360
|
+
Before deploying to production:
|
|
361
|
+
|
|
362
|
+
- [ ] API keys stored in environment variables (not code)
|
|
363
|
+
- [ ] SDK only used server-side (not in browser/mobile)
|
|
364
|
+
- [ ] All ad content sanitized before rendering (XSS prevention)
|
|
365
|
+
- [ ] URLs validated before use (phishing prevention)
|
|
366
|
+
- [ ] Rate limiting implemented (abuse prevention)
|
|
367
|
+
- [ ] User input validated and sanitized
|
|
368
|
+
- [ ] Error messages don't leak sensitive information
|
|
369
|
+
- [ ] HTTPS enforced for all API calls
|
|
370
|
+
- [ ] Dependencies regularly updated
|
|
371
|
+
- [ ] Security headers configured (CSP, X-Frame-Options, etc.)
|
|
372
|
+
|
|
373
|
+
---
|
|
49
374
|
|
|
50
|
-
|
|
375
|
+
**Last Updated:** 2025-02-01
|
|
376
|
+
**SDK Version:** 0.1.1+
|
package/dist/index.d.mts
CHANGED
|
@@ -141,6 +141,7 @@ interface APIError {
|
|
|
141
141
|
}
|
|
142
142
|
interface SDKConfig {
|
|
143
143
|
apiKey: string;
|
|
144
|
+
supabaseAnonKey?: string;
|
|
144
145
|
baseUrl?: string;
|
|
145
146
|
timeoutMs?: number;
|
|
146
147
|
maxRetries?: number;
|
|
@@ -220,6 +221,77 @@ interface CreateClickEventParams {
|
|
|
220
221
|
* @returns EventIngestRequest ready to pass to client.track()
|
|
221
222
|
*/
|
|
222
223
|
declare function createClickEvent(params: CreateClickEventParams): EventIngestRequest;
|
|
224
|
+
/**
|
|
225
|
+
* Options for URL sanitization
|
|
226
|
+
*/
|
|
227
|
+
interface SanitizeURLOptions {
|
|
228
|
+
/**
|
|
229
|
+
* Allow HTTP URLs (default: false, HTTPS only)
|
|
230
|
+
*/
|
|
231
|
+
allowHttp?: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Allow tel: links (default: true)
|
|
234
|
+
*/
|
|
235
|
+
allowTel?: boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Allow mailto: links (default: true)
|
|
238
|
+
*/
|
|
239
|
+
allowMailto?: boolean;
|
|
240
|
+
/**
|
|
241
|
+
* Callback for validation warnings (instead of console.warn)
|
|
242
|
+
*/
|
|
243
|
+
onWarning?: (message: string, context: {
|
|
244
|
+
url?: string;
|
|
245
|
+
protocol?: string;
|
|
246
|
+
}) => void;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Escape HTML special characters to prevent XSS attacks.
|
|
250
|
+
*
|
|
251
|
+
* Handles null/undefined safely by returning empty string.
|
|
252
|
+
* Escapes: & < > " ' / `
|
|
253
|
+
*
|
|
254
|
+
* Use this when displaying ad content (title, body, cta) in HTML contexts.
|
|
255
|
+
*
|
|
256
|
+
* @param text - Text to escape (can be null/undefined)
|
|
257
|
+
* @returns Escaped HTML string (empty string if input is null/undefined)
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* const safeTitle = escapeHTML(unit.suggestion.title);
|
|
262
|
+
* element.innerHTML = safeTitle; // Safe from XSS
|
|
263
|
+
*
|
|
264
|
+
* escapeHTML(null); // Returns ''
|
|
265
|
+
* escapeHTML('<script>alert(1)</script>'); // Returns '<script>alert(1)</script>'
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
declare function escapeHTML(text: string | null | undefined): string;
|
|
269
|
+
/**
|
|
270
|
+
* Sanitize and validate a URL to prevent XSS and phishing attacks.
|
|
271
|
+
*
|
|
272
|
+
* Handles null/undefined safely by returning null.
|
|
273
|
+
* Blocks dangerous protocols like javascript:, data:, file:, blob:, vbscript:.
|
|
274
|
+
* Validates URL length to prevent DoS attacks (max 2048 chars).
|
|
275
|
+
* Handles protocol-relative URLs (//example.com).
|
|
276
|
+
*
|
|
277
|
+
* @param url - URL to sanitize (can be null/undefined)
|
|
278
|
+
* @param options - Sanitization options
|
|
279
|
+
* @returns Sanitized URL string, or null if invalid/dangerous
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
284
|
+
* if (safeURL) {
|
|
285
|
+
* window.open(safeURL, '_blank');
|
|
286
|
+
* }
|
|
287
|
+
*
|
|
288
|
+
* sanitizeURL(null); // Returns null
|
|
289
|
+
* sanitizeURL('javascript:alert(1)'); // Returns null (blocked)
|
|
290
|
+
* sanitizeURL('https://example.com'); // Returns 'https://example.com'
|
|
291
|
+
* sanitizeURL('http://example.com', { allowHttp: true }); // Returns 'http://example.com'
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
declare function sanitizeURL(url: string | null | undefined, options?: SanitizeURLOptions): string | null;
|
|
223
295
|
|
|
224
296
|
/**
|
|
225
297
|
* Main SDK client for AttentionMarket Agent Ads API.
|
|
@@ -229,6 +301,10 @@ declare function createClickEvent(params: CreateClickEventParams): EventIngestRe
|
|
|
229
301
|
declare class AttentionMarketClient {
|
|
230
302
|
private http;
|
|
231
303
|
constructor(config: SDKConfig);
|
|
304
|
+
/**
|
|
305
|
+
* Validate SDK configuration for security
|
|
306
|
+
*/
|
|
307
|
+
private validateConfig;
|
|
232
308
|
/**
|
|
233
309
|
* Request a sponsored unit decision for an agent opportunity.
|
|
234
310
|
* Returns the full DecideResponse including status, ttl_ms, and all units.
|
|
@@ -398,4 +474,4 @@ declare class TimeoutError extends AttentionMarketError {
|
|
|
398
474
|
constructor(message?: string);
|
|
399
475
|
}
|
|
400
476
|
|
|
401
|
-
export { type APIError, APIRequestError, type AdUnit, type AgentSignupRequest, type AgentSignupResponse, AttentionMarketClient, AttentionMarketError, type Constraints, type Context, type CreateClickEventParams, type CreateImpressionEventParams, type CreateOpportunityParams, type DecideRequest, type DecideResponse, type Disclosure, type EventIngestRequest, type EventIngestResponse, type EventType, type Intent, MockAttentionMarketClient, type MockClientConfig, NetworkError, type Opportunity, type Placement, type PlacementType, type PolicyResponse, type Privacy, type SDKConfig, type SponsoredSuggestion, type SponsoredTool, TimeoutError, type ToolCall, type Tracking, createClickEvent, createImpressionEvent, createOpportunity, generateTimestamp, generateUUID };
|
|
477
|
+
export { type APIError, APIRequestError, type AdUnit, type AgentSignupRequest, type AgentSignupResponse, AttentionMarketClient, AttentionMarketError, type Constraints, type Context, type CreateClickEventParams, type CreateImpressionEventParams, type CreateOpportunityParams, type DecideRequest, type DecideResponse, type Disclosure, type EventIngestRequest, type EventIngestResponse, type EventType, type Intent, MockAttentionMarketClient, type MockClientConfig, NetworkError, type Opportunity, type Placement, type PlacementType, type PolicyResponse, type Privacy, type SDKConfig, type SanitizeURLOptions, type SponsoredSuggestion, type SponsoredTool, TimeoutError, type ToolCall, type Tracking, createClickEvent, createImpressionEvent, createOpportunity, escapeHTML, generateTimestamp, generateUUID, sanitizeURL };
|
package/dist/index.d.ts
CHANGED
|
@@ -141,6 +141,7 @@ interface APIError {
|
|
|
141
141
|
}
|
|
142
142
|
interface SDKConfig {
|
|
143
143
|
apiKey: string;
|
|
144
|
+
supabaseAnonKey?: string;
|
|
144
145
|
baseUrl?: string;
|
|
145
146
|
timeoutMs?: number;
|
|
146
147
|
maxRetries?: number;
|
|
@@ -220,6 +221,77 @@ interface CreateClickEventParams {
|
|
|
220
221
|
* @returns EventIngestRequest ready to pass to client.track()
|
|
221
222
|
*/
|
|
222
223
|
declare function createClickEvent(params: CreateClickEventParams): EventIngestRequest;
|
|
224
|
+
/**
|
|
225
|
+
* Options for URL sanitization
|
|
226
|
+
*/
|
|
227
|
+
interface SanitizeURLOptions {
|
|
228
|
+
/**
|
|
229
|
+
* Allow HTTP URLs (default: false, HTTPS only)
|
|
230
|
+
*/
|
|
231
|
+
allowHttp?: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Allow tel: links (default: true)
|
|
234
|
+
*/
|
|
235
|
+
allowTel?: boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Allow mailto: links (default: true)
|
|
238
|
+
*/
|
|
239
|
+
allowMailto?: boolean;
|
|
240
|
+
/**
|
|
241
|
+
* Callback for validation warnings (instead of console.warn)
|
|
242
|
+
*/
|
|
243
|
+
onWarning?: (message: string, context: {
|
|
244
|
+
url?: string;
|
|
245
|
+
protocol?: string;
|
|
246
|
+
}) => void;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Escape HTML special characters to prevent XSS attacks.
|
|
250
|
+
*
|
|
251
|
+
* Handles null/undefined safely by returning empty string.
|
|
252
|
+
* Escapes: & < > " ' / `
|
|
253
|
+
*
|
|
254
|
+
* Use this when displaying ad content (title, body, cta) in HTML contexts.
|
|
255
|
+
*
|
|
256
|
+
* @param text - Text to escape (can be null/undefined)
|
|
257
|
+
* @returns Escaped HTML string (empty string if input is null/undefined)
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* const safeTitle = escapeHTML(unit.suggestion.title);
|
|
262
|
+
* element.innerHTML = safeTitle; // Safe from XSS
|
|
263
|
+
*
|
|
264
|
+
* escapeHTML(null); // Returns ''
|
|
265
|
+
* escapeHTML('<script>alert(1)</script>'); // Returns '<script>alert(1)</script>'
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
declare function escapeHTML(text: string | null | undefined): string;
|
|
269
|
+
/**
|
|
270
|
+
* Sanitize and validate a URL to prevent XSS and phishing attacks.
|
|
271
|
+
*
|
|
272
|
+
* Handles null/undefined safely by returning null.
|
|
273
|
+
* Blocks dangerous protocols like javascript:, data:, file:, blob:, vbscript:.
|
|
274
|
+
* Validates URL length to prevent DoS attacks (max 2048 chars).
|
|
275
|
+
* Handles protocol-relative URLs (//example.com).
|
|
276
|
+
*
|
|
277
|
+
* @param url - URL to sanitize (can be null/undefined)
|
|
278
|
+
* @param options - Sanitization options
|
|
279
|
+
* @returns Sanitized URL string, or null if invalid/dangerous
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const safeURL = sanitizeURL(unit.suggestion.action_url);
|
|
284
|
+
* if (safeURL) {
|
|
285
|
+
* window.open(safeURL, '_blank');
|
|
286
|
+
* }
|
|
287
|
+
*
|
|
288
|
+
* sanitizeURL(null); // Returns null
|
|
289
|
+
* sanitizeURL('javascript:alert(1)'); // Returns null (blocked)
|
|
290
|
+
* sanitizeURL('https://example.com'); // Returns 'https://example.com'
|
|
291
|
+
* sanitizeURL('http://example.com', { allowHttp: true }); // Returns 'http://example.com'
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
declare function sanitizeURL(url: string | null | undefined, options?: SanitizeURLOptions): string | null;
|
|
223
295
|
|
|
224
296
|
/**
|
|
225
297
|
* Main SDK client for AttentionMarket Agent Ads API.
|
|
@@ -229,6 +301,10 @@ declare function createClickEvent(params: CreateClickEventParams): EventIngestRe
|
|
|
229
301
|
declare class AttentionMarketClient {
|
|
230
302
|
private http;
|
|
231
303
|
constructor(config: SDKConfig);
|
|
304
|
+
/**
|
|
305
|
+
* Validate SDK configuration for security
|
|
306
|
+
*/
|
|
307
|
+
private validateConfig;
|
|
232
308
|
/**
|
|
233
309
|
* Request a sponsored unit decision for an agent opportunity.
|
|
234
310
|
* Returns the full DecideResponse including status, ttl_ms, and all units.
|
|
@@ -398,4 +474,4 @@ declare class TimeoutError extends AttentionMarketError {
|
|
|
398
474
|
constructor(message?: string);
|
|
399
475
|
}
|
|
400
476
|
|
|
401
|
-
export { type APIError, APIRequestError, type AdUnit, type AgentSignupRequest, type AgentSignupResponse, AttentionMarketClient, AttentionMarketError, type Constraints, type Context, type CreateClickEventParams, type CreateImpressionEventParams, type CreateOpportunityParams, type DecideRequest, type DecideResponse, type Disclosure, type EventIngestRequest, type EventIngestResponse, type EventType, type Intent, MockAttentionMarketClient, type MockClientConfig, NetworkError, type Opportunity, type Placement, type PlacementType, type PolicyResponse, type Privacy, type SDKConfig, type SponsoredSuggestion, type SponsoredTool, TimeoutError, type ToolCall, type Tracking, createClickEvent, createImpressionEvent, createOpportunity, generateTimestamp, generateUUID };
|
|
477
|
+
export { type APIError, APIRequestError, type AdUnit, type AgentSignupRequest, type AgentSignupResponse, AttentionMarketClient, AttentionMarketError, type Constraints, type Context, type CreateClickEventParams, type CreateImpressionEventParams, type CreateOpportunityParams, type DecideRequest, type DecideResponse, type Disclosure, type EventIngestRequest, type EventIngestResponse, type EventType, type Intent, MockAttentionMarketClient, type MockClientConfig, NetworkError, type Opportunity, type Placement, type PlacementType, type PolicyResponse, type Privacy, type SDKConfig, type SanitizeURLOptions, type SponsoredSuggestion, type SponsoredTool, TimeoutError, type ToolCall, type Tracking, createClickEvent, createImpressionEvent, createOpportunity, escapeHTML, generateTimestamp, generateUUID, sanitizeURL };
|