Skip to main content

Service Development Guide

A comprehensive guide for developing TypeScript service definitions for CSP Kit. This guide covers everything from research to testing.

Table of Contents​

  1. Overview
  2. Service Research
  3. Development Workflow
  4. Testing Services
  5. Common Patterns
  6. Troubleshooting
  7. Best Practices

Overview​

Services in CSP Kit are TypeScript modules that define the Content Security Policy requirements for third-party services. Each service exports a configuration object that the generator uses to build CSP headers.

What Makes a Good Service Definition?​

  • Accurate: Contains all necessary domains and directives
  • Minimal: Only includes what's actually required
  • Documented: Has clear notes about special requirements
  • Tested: Verified against real implementations
  • Maintained: Updated when services change

Service Research​

Step 1: Gather Information​

Before writing code, research the service thoroughly:

Official Documentation​

Look for CSP-specific documentation:

# Search terms to use:
- "Content Security Policy"
- "CSP requirements"
- "security headers"
- "domain whitelist"
- "allowed origins"

Example sources:

Network Analysis​

Use browser DevTools to analyze actual network requests:

  1. Create a test page with the service integration:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Service Test Page</title>
<!-- Add service integration code here -->
</head>
<body>
<h1>Testing Service CSP Requirements</h1>
</body>
</html>
  1. Open DevTools → Network tab
  2. Filter by domain to see all requests
  3. Note all unique domains and their purposes

CSP Testing​

Test with restrictive CSP to find requirements:

<!-- Start with very restrictive CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'" />

<!-- Add service code and see what breaks -->
<!-- Check console for CSP violations -->

Step 2: Document Your Findings​

Create a research document:

# Service: [Service Name]

## Domains Used

- `https://cdn.service.com` - Main JavaScript library
- `https://api.service.com` - API endpoints
- `https://assets.service.com` - Images and static assets
- `wss://realtime.service.com` - WebSocket connections

## CSP Requirements

- `script-src`: https://cdn.service.com
- `connect-src`: https://api.service.com, wss://realtime.service.com
- `img-src`: https://assets.service.com

## Special Requirements

- Requires 'unsafe-inline' for styles (try to avoid if possible)
- Uses dynamic script injection
- Needs frame-src for embedded widgets

## Test URLs

- Demo page: https://service.com/demo
- Documentation: https://docs.service.com/integrate

Development Workflow​

Step 1: Create Service File​

Navigate to the appropriate category:

cd packages/data/src/services/analytics/  # or appropriate category

Create your service file:

// new-analytics-service.ts
import { defineService } from '../../service-types.js';
import { ServiceCategory } from '../../types.js';

export const NewAnalyticsService = defineService({
// Service identity
id: 'new-analytics-service',
name: 'New Analytics Service',
category: ServiceCategory.ANALYTICS,

// Description and documentation
description: 'Advanced analytics platform for tracking user behavior and conversions',
website: 'https://newanalytics.com',

// CSP directives based on research
directives: {
'script-src': ['https://cdn.newanalytics.com'],
'connect-src': ['https://api.newanalytics.com', 'https://collect.newanalytics.com'],
'img-src': ['https://pixel.newanalytics.com'],
},

// Documentation
officialDocs: [
'https://docs.newanalytics.com/security/csp',
'https://help.newanalytics.com/integrate',
],

// Implementation notes
notes:
'Requires script-src for tracking library. The collect endpoint is for event data. Pixel endpoint is used for conversion tracking.',

// Optional: Aliases for easier discovery
aliases: ['new-analytics', 'na'],

// Optional: Special requirements
requiresDynamic: true, // If service injects scripts dynamically

// Timestamps
lastUpdated: new Date().toISOString(),
verifiedAt: new Date().toISOString(),
});

Step 2: Add to Category Index​

Export your service from the category index:

// services/analytics/index.ts
export * from './google-analytics.js';
export * from './amplitude.js';
export * from './new-analytics-service.js'; // Add this line

Step 3: Create Tests​

Create a test file for your service:

// services/analytics/__tests__/new-analytics-service.test.ts
import { describe, it, expect } from 'vitest';
import { NewAnalyticsService } from '../new-analytics-service.js';
import { validateService } from '../../../test-utils.js';

describe('NewAnalyticsService', () => {
it('should have valid structure', () => {
expect(validateService(NewAnalyticsService)).toBe(true);
});

it('should have correct ID and name', () => {
expect(NewAnalyticsService.id).toBe('new-analytics-service');
expect(NewAnalyticsService.name).toBe('New Analytics Service');
});

it('should include all required domains', () => {
const { directives } = NewAnalyticsService;

expect(directives['script-src']).toContain('https://cdn.newanalytics.com');
expect(directives['connect-src']).toContain('https://api.newanalytics.com');
expect(directives['connect-src']).toContain('https://collect.newanalytics.com');
expect(directives['img-src']).toContain('https://pixel.newanalytics.com');
});

it('should have valid URLs', () => {
const allUrls = Object.values(NewAnalyticsService.directives).flat();

allUrls.forEach(url => {
expect(url).toMatch(/^https?:\/\/.+/);
});
});

it('should have proper documentation', () => {
expect(NewAnalyticsService.website).toBeTruthy();
expect(NewAnalyticsService.description).toBeTruthy();
expect(NewAnalyticsService.officialDocs).toHaveLength(2);
});
});

Step 4: Integration Testing​

Test your service with the actual generator:

// test-integration.ts
import { generateCSP } from '@csp-kit/generator';
import { NewAnalyticsService } from './packages/data/src/services/analytics/new-analytics-service.js';

// Test basic generation
const result = generateCSP({
services: [NewAnalyticsService],
});

console.log('Generated CSP:', result.header);
console.log('Included services:', result.includedServices);
console.log('Warnings:', result.warnings);

// Test with other services
const multiResult = generateCSP({
services: [NewAnalyticsService, GoogleAnalytics, Stripe],
});

console.log('Multi-service CSP:', multiResult.header);

// Test with nonce
const nonceResult = generateCSP({
services: [NewAnalyticsService],
nonce: true,
});

console.log('With nonce:', nonceResult.header);

Testing Services​

Unit Tests​

Every service should have comprehensive unit tests:

describe('Service Tests', () => {
// Structure validation
it('should have all required fields', () => {
expect(Service.id).toBeDefined();
expect(Service.name).toBeDefined();
expect(Service.category).toBeDefined();
expect(Service.directives).toBeDefined();
});

// ID format
it('should have kebab-case ID', () => {
expect(Service.id).toMatch(/^[a-z]+(-[a-z]+)*$/);
});

// URL validation
it('should have valid HTTPS URLs', () => {
Object.values(Service.directives)
.flat()
.forEach(url => {
if (!url.startsWith("'")) {
// Skip CSP keywords
expect(url).toMatch(/^https:\/\//);
}
});
});

// No duplicates
it('should not have duplicate URLs in directives', () => {
Object.entries(Service.directives).forEach(([directive, urls]) => {
const unique = new Set(urls);
expect(urls.length).toBe(unique.size);
});
});
});

Manual Testing​

Create a real-world test page:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>CSP Test - New Analytics Service</title>

<!-- Apply generated CSP -->
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' https://cdn.newanalytics.com; connect-src 'self' https://api.newanalytics.com https://collect.newanalytics.com; img-src 'self' https://pixel.newanalytics.com"
/>

<!-- Service integration code -->
<script src="https://cdn.newanalytics.com/analytics.js"></script>
<script>
NewAnalytics.init('YOUR_API_KEY');
NewAnalytics.track('page_view');
</script>
</head>
<body>
<h1>Testing New Analytics Service CSP</h1>
<p>Check console for CSP violations</p>

<button onclick="NewAnalytics.track('button_click')">Test Event Tracking</button>
</body>
</html>

Automated Validation​

Use the CLI tools to validate:

# Build the package
pnpm build --filter @csp-kit/data

# Run validation
pnpm validate-services

# Test specific service
pnpm test new-analytics-service

Common Patterns​

Analytics Services​

Common requirements for analytics services:

export const AnalyticsPattern = defineService({
id: 'analytics-pattern',
name: 'Analytics Pattern',
category: ServiceCategory.ANALYTICS,
directives: {
'script-src': [
'https://cdn.analytics.com', // Main tracking library
],
'connect-src': [
'https://api.analytics.com', // Event collection
'https://collect.analytics.com', // Beacon API
],
'img-src': [
'https://pixel.analytics.com', // Tracking pixels
],
},
requiresDynamic: true, // Most analytics inject scripts
});

Payment Services​

Common requirements for payment processors:

export const PaymentPattern = defineService({
id: 'payment-pattern',
name: 'Payment Pattern',
category: ServiceCategory.PAYMENT,
directives: {
'script-src': [
'https://js.payment.com', // Payment SDK
],
'frame-src': [
'https://checkout.payment.com', // Embedded checkout
'https://3ds.payment.com', // 3D Secure
],
'connect-src': [
'https://api.payment.com', // API calls
],
'img-src': [
'https://assets.payment.com', // Card brand logos
],
},
notes: 'frame-src required for embedded checkout and 3D Secure authentication',
});

Chat/Support Services​

Common requirements for chat widgets:

export const ChatPattern = defineService({
id: 'chat-pattern',
name: 'Chat Pattern',
category: ServiceCategory.CHAT,
directives: {
'script-src': [
'https://widget.chat.com', // Chat widget loader
],
'frame-src': [
'https://widget.chat.com', // Chat iframe
'https://app.chat.com', // Full chat interface
],
'connect-src': [
'https://api.chat.com', // REST API
'wss://realtime.chat.com', // WebSocket for real-time
],
'img-src': [
'https://static.chat.com', // Avatars
'https://uploads.chat.com', // User uploads
],
'media-src': [
'https://media.chat.com', // Voice/video calls
],
},
});

CDN Services​

Common requirements for CDNs:

export const CDNPattern = defineService({
id: 'cdn-pattern',
name: 'CDN Pattern',
category: ServiceCategory.CDN,
directives: {
'script-src': ['https://cdn.example.com'],
'style-src': ['https://cdn.example.com'],
'img-src': ['https://cdn.example.com'],
'font-src': ['https://cdn.example.com'],
'media-src': ['https://cdn.example.com'],
},
notes: 'General-purpose CDN serving multiple content types',
});

Troubleshooting​

Common Issues​

1. Service Not Working​

Symptom: Integration broken despite CSP rules Solution: Check for:

  • Missing domains (check ALL network requests)
  • Dynamic script injection requiring 'strict-dynamic'
  • Inline scripts/styles needing nonce
  • WebSocket connections (wss://)

2. Too Many Warnings​

Symptom: Validation warnings about wildcards Solution:

  • Be more specific with domains
  • List known subdomains explicitly
  • Only use wildcards when absolutely necessary

3. Conflicting Services​

Symptom: Services interfere with each other Solution: Add conflict detection:

export const ServiceA = defineService({
id: 'service-a',
// ... other fields
conflicts: ['service-b'], // Cannot be used with service-b
});

4. Dynamic Requirements​

Symptom: Service works differently in different contexts Solution: Document variations:

export const DynamicService = defineService({
id: 'dynamic-service',
// ... other fields
notes:
'Basic integration requires script-src only. If using advanced features (video calls), also needs media-src and additional connect-src endpoints.',
validate: directives => {
const warnings = [];
if (!directives['media-src']?.includes('https://media.service.com')) {
warnings.push('Video features require media-src for https://media.service.com');
}
return { warnings };
},
});

Best Practices​

1. Research Thoroughly​

  • Always check official documentation first
  • Test with real implementation
  • Monitor for service updates
  • Document edge cases

2. Be Conservative​

  • Only include domains you've verified
  • Start minimal, add as needed
  • Prefer specific domains over wildcards
  • Document why each domain is needed

3. Maintain Quality​

  • Write comprehensive tests
  • Update timestamps when verifying
  • Add helpful notes for users
  • Keep descriptions concise but clear

4. Consider Security​

  • Never include 'unsafe-inline' unless absolutely required
  • Document security implications
  • Suggest alternatives when possible
  • Warn about risky configurations

5. Think About Users​

  • Use clear, recognizable names
  • Add helpful aliases
  • Write descriptive notes
  • Include migration guidance for deprecated services

Service Lifecycle​

1. Initial Development​

  • Research requirements
  • Create service definition
  • Write tests
  • Submit PR

2. Review Process​

  • Automated validation
  • Manual testing
  • Security review
  • Documentation check

3. Maintenance​

  • Monitor for changes
  • Update when needed
  • Handle deprecations
  • Respond to issues

4. Deprecation​

When a service is no longer needed:

export const DeprecatedService = defineService({
id: 'deprecated-service',
name: 'Deprecated Service',
// ... other fields
deprecated: {
since: '2025-01-01',
message: 'This service has shut down',
alternative: 'new-service', // Suggest replacement
},
});

Contributing Your Service​

1. Fork and Clone​

git clone https://github.com/yourusername/csp-kit.git
cd csp-kit
pnpm install

2. Create Feature Branch​

git checkout -b add-new-analytics-service

3. Develop and Test​

# Build
pnpm build --filter @csp-kit/data

# Test
pnpm test --filter @csp-kit/data

# Manual test
pnpm tsx test-integration.ts

4. Submit PR​

git add .
git commit -m "feat(data): add NewAnalyticsService definition"
git push origin add-new-analytics-service

Include in your PR:

  • Service definition file
  • Tests
  • Export from category index
  • Evidence of testing (screenshots, test URLs)
  • Any special notes for reviewers

Resources​


Thank you for contributing to CSP Kit! Your service definitions help developers implement security best practices more easily. 🚀