@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 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. 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+
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 '&lt;script&gt;alert(1)&lt;/script&gt;'
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 '&lt;script&gt;alert(1)&lt;/script&gt;'
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 };