@the_ro_show/agent-ads-sdk 0.1.0 → 0.1.2

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 AttentionMarket
3
+ Copyright (c) 2025 AttentionMarket
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -55,6 +55,137 @@ 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
+
115
+ ## Testing Without Advertiser Data
116
+
117
+ Use `MockAttentionMarketClient` to test your integration without needing real ad campaigns.
118
+
119
+ ```typescript
120
+ import { MockAttentionMarketClient, createOpportunity, generateUUID } from '@the_ro_show/agent-ads-sdk';
121
+
122
+ // Use mock client during development
123
+ const client = new MockAttentionMarketClient({
124
+ latencyMs: 100, // Simulate API latency
125
+ fillRate: 1.0, // 100% fill rate for testing
126
+ verbose: true, // Log mock activity
127
+ });
128
+
129
+ const opportunity = createOpportunity({
130
+ taxonomy: 'local_services.movers.quote',
131
+ country: 'US',
132
+ language: 'en',
133
+ platform: 'web',
134
+ query: 'Find movers in Brooklyn',
135
+ });
136
+
137
+ const unit = await client.decide({
138
+ request_id: generateUUID(),
139
+ agent_id: 'agt_test',
140
+ placement: { type: 'sponsored_suggestion', surface: 'chat_response' },
141
+ opportunity,
142
+ });
143
+
144
+ // Returns realistic mock ad data immediately
145
+ if (unit && unit.unit_type === 'sponsored_suggestion') {
146
+ console.log(unit.suggestion.title); // "Professional Moving Services - Same Day Available"
147
+ }
148
+ ```
149
+
150
+ **Available mock taxonomies:**
151
+ - `local_services.movers.quote`
152
+ - `local_services.restaurants.search`
153
+ - `local_services.plumbers.quote`
154
+ - `local_services.electricians.quote`
155
+ - `local_services.cleaners.quote`
156
+ - `shopping.electronics.search`
157
+
158
+ **Add custom mock ads:**
159
+
160
+ ```typescript
161
+ client.addMockUnit('your.custom.taxonomy', {
162
+ unit_id: 'unit_custom_001',
163
+ unit_type: 'sponsored_suggestion',
164
+ disclosure: { label: 'Sponsored', sponsor_name: 'Your Sponsor' },
165
+ tracking: { token: 'trk_test', impression_url: '...', click_url: '...' },
166
+ suggestion: {
167
+ title: 'Your Ad Title',
168
+ body: 'Your ad body text',
169
+ cta: 'Call to Action',
170
+ action_url: 'https://example.com',
171
+ },
172
+ });
173
+ ```
174
+
175
+ **Run the full test suite:**
176
+
177
+ ```bash
178
+ npx tsx examples/test-with-mocks.ts
179
+ ```
180
+
181
+ **Switch to production:**
182
+
183
+ ```typescript
184
+ const client = process.env.NODE_ENV === 'production'
185
+ ? new AttentionMarketClient({ apiKey: process.env.ATTENTIONMARKET_API_KEY })
186
+ : new MockAttentionMarketClient();
187
+ ```
188
+
58
189
  ## Agent Integration Examples
59
190
 
60
191
  ### Minimal Examples (< 80 lines)
@@ -74,10 +205,12 @@ For production integrations:
74
205
  - **[Claude (Anthropic)](./examples/claude-tool-use-full.ts)** - Complete tool use pattern with schemas and tracking
75
206
  - **[OpenAI GPT](./examples/openai-function-calling-full.ts)** - Complete function calling with integration checklist
76
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
77
209
 
78
210
  Run any example with:
79
211
  ```bash
80
212
  npx tsx examples/claude-tool-use-minimal.ts
213
+ npx tsx examples/safe-web-rendering.ts
81
214
  ```
82
215
 
83
216
  ## Full Example with Raw Response
@@ -334,6 +467,42 @@ import { generateUUID } from '@the_ro_show/agent-ads-sdk';
334
467
  const requestId = generateUUID();
335
468
  ```
336
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
+
337
506
  ## Features
338
507
 
339
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. Follow these best practices:
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 systems (AWS Secrets Manager, GitHub Secrets, etc.)
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 security@attentionmarket.com with:
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
- Do not open public GitHub issues for security vulnerabilities.
375
+ **Last Updated:** 2025-02-01
376
+ **SDK Version:** 0.1.1+