XARF JavaScript/TypeScript Library
Official JavaScript/TypeScript library for creating, validating, and processing XARF (eXtended Abuse Reporting Format) reports.
Installation
npm
npm install @xarf/xarf
yarn
yarn add @xarf/xarf
pnpm
pnpm add @xarf/xarf
Requirements:
- Node.js 16+ or modern browser (ES2020+)
- TypeScript 4.5+ (optional, for type definitions)
Note: Alpha release available. Star the GitHub repository for updates.
Quick Start
Creating a Report
import { XARFReport, XARFValidator } from 'xarf';
// Create a new XARF report
const report = new XARFReport({
xarf_version: '4.0.0',
report_id: '550e8400-e29b-41d4-a716-446655440000',
timestamp: new Date().toISOString(),
reporter: {
org: 'Security Operations',
contact: '[email protected]',
domain: 'example.com'
},
sender: {
org: 'Security Operations',
contact: '[email protected]',
domain: 'example.com'
},
source_identifier: '192.0.2.100',
category: 'connection',
type: 'ddos',
description: 'DDoS attack detected from source IP'
});
// Validate
const validator = new XARFValidator();
if (await validator.validate(report)) {
console.log('✓ Report is valid!');
}
// Export to JSON
const json = report.toJSON();
console.log(JSON.stringify(json, null, 2));
Loading from JSON
import { XARFReport } from 'xarf';
// Load from JSON string
const jsonData = '{"xarf_version": "4.0.0", ...}';
const report = XARFReport.fromJSON(jsonData);
// Load from file (Node.js)
import { readFile } from 'fs/promises';
const fileContent = await readFile('report.json', 'utf-8');
const report = XARFReport.fromJSON(fileContent);
// Validate
if (await report.validate()) {
console.log(`Loaded report: ${report.report_id}`);
}
Core Features
- Report Creation and Validation - Type-safe report creation with automatic validation
- Schema Validation - Full JSON schema validation against XARF 4.0 specification
- Evidence Handling - Base64 encoding, hashing (SHA-256/SHA-512), and integrity verification
- Format Conversion - JSON serialization with pretty-printing support
- Type Safety - Complete TypeScript definitions included
- Promise-based API - Modern async/await support throughout
- Browser & Node.js - Works in both environments
- Stream Processing - Handle large datasets with Node.js streams
API Reference
XARFReport Class
Main class for creating and manipulating XARF reports.
Constructor
constructor(data: XARFReportData)
Parameters:
data(XARFReportData): Report data object
Example:
const report = new XARFReport({
xarf_version: '4.0.0',
report_id: '550e8400-e29b-41d4-a716-446655440000',
timestamp: new Date().toISOString(),
reporter: {
org: 'Security Ops',
contact: '[email protected]',
domain: 'example.com'
},
sender: {
org: 'Security Ops',
contact: '[email protected]',
domain: 'example.com'
},
source_identifier: '192.0.2.100',
category: 'connection',
type: 'ddos'
});
Methods
validate(options?: ValidationOptions): Promise<boolean>
Validate the report against the JSON schema.
const isValid = await report.validate();
if (!isValid) {
console.error('Validation errors:', report.validationErrors);
}
Parameters:
options(ValidationOptions, optional): Validation configuration
Returns: Promise<boolean> - True if valid, false otherwise
toJSON(pretty?: boolean): string
Export report to JSON string.
const jsonString = report.toJSON(true); // Pretty-printed
Parameters:
pretty(boolean, optional): Pretty-print with 2-space indentation
Returns: string - JSON representation
toObject(): XARFReportData
Convert report to plain JavaScript object.
const data = report.toObject();
Returns: XARFReportData - Report as plain object
static fromJSON(json: string): XARFReport
Create report from JSON string (static method).
const report = XARFReport.fromJSON('{"xarf_version": "4.0.0", ...}');
Parameters:
json(string): JSON string
Returns: XARFReport instance
static fromObject(data: XARFReportData): XARFReport
Create report from plain object (static method).
const report = XARFReport.fromObject({
xarf_version: '4.0.0',
// ...
});
Parameters:
data(XARFReportData): Plain object
Returns: XARFReport instance
addEvidence(evidence: Evidence): void
Add evidence to the report.
report.addEvidence({
content_type: 'text/plain',
description: 'Server logs',
payload: 'YmFzZTY0IGVuY29kZWQgZGF0YQ==',
hash: {
algorithm: 'sha256',
value: 'abc123...'
}
});
Parameters:
evidence(Evidence): Evidence object
XARFValidator Class
Validator for XARF reports.
const validator = new XARFValidator();
const isValid = await validator.validate(report);
Evidence Class
Handles evidence attachment and verification.
import { EvidenceHelper } from 'xarf';
const evidence = await EvidenceHelper.createFromFile(
'application/pdf',
'Phishing email screenshot',
'./evidence.pdf'
);
Error Classes
import {
ValidationError,
ParseError,
SchemaError
} from 'xarf';
Type Definitions
The library includes full TypeScript definitions:
interface XARFReportData {
xarf_version: string;
report_id: string;
timestamp: string;
reporter: Reporter;
source_identifier: string;
category: string;
type: string;
description?: string;
severity?: 'low' | 'medium' | 'high' | 'critical';
evidence?: Evidence[];
technical_details?: Record<string, any>;
[key: string]: any;
}
interface Reporter {
org: string;
contact: string;
type: 'automated' | 'manual' | 'ai';
url?: string;
}
interface Evidence {
content_type: string;
description: string;
payload: string;
hash?: Hash;
timestamp?: string;
}
interface Hash {
algorithm: 'sha256' | 'sha512' | 'md5';
value: string;
}
interface ValidationOptions {
strict?: boolean;
checkRecommended?: boolean;
allowAdditional?: boolean;
}
Examples
Creating a DDoS Report
import { XARFReport } from 'xarf';
const ddosReport = new XARFReport({
xarf_version: '4.0.0',
report_id: crypto.randomUUID(),
timestamp: new Date().toISOString(),
reporter: {
org: 'Network Security Team',
contact: '[email protected]',
domain: 'example.com'
},
sender: {
org: 'Network Security Team',
contact: '[email protected]',
domain: 'example.com'
},
source_identifier: '203.0.113.50',
category: 'connection',
type: 'ddos',
description: 'Volumetric DDoS attack detected',
protocol: 'udp',
destination_port: 53,
peak_pps: 150000,
peak_bps: 1200000000,
duration_seconds: 300
});
if (await ddosReport.validate()) {
await submitReport(ddosReport);
}
Adding Evidence
import { EvidenceHelper } from 'xarf';
// Create evidence with automatic hashing
const evidence = await EvidenceHelper.createFromFile(
'application/pdf',
'Phishing email screenshot',
'./evidence.pdf'
);
console.log(`SHA-256: ${evidence.hash.value}`);
// Add to report
report.addEvidence(evidence);
// Verify evidence integrity
const isValid = await EvidenceHelper.verify(evidence);
console.log(`Evidence integrity: ${isValid ? '✓' : '✗'}`);
Batch Validation
import { XARFBatch } from 'xarf';
const batch = new XARFBatch();
// Add reports
const jsonFiles = await glob('reports/*.json');
for (const file of jsonFiles) {
const content = await readFile(file, 'utf-8');
batch.addReport(content);
}
// Validate all
const results = await batch.validateAll();
console.log(`Valid: ${results.valid.length}`);
console.log(`Invalid: ${results.invalid.length}`);
// Process valid reports
for (const report of results.valid) {
await processReport(report);
}
// Log invalid reports
for (const error of results.invalid) {
console.error(`${error.reportId}: ${error.errors.join(', ')}`);
}
Custom Fields
const report = new XARFReport({
xarf_version: '4.0.0',
report_id: crypto.randomUUID(),
timestamp: new Date().toISOString(),
reporter: {
org: 'Security Team',
contact: '[email protected]',
domain: 'example.com'
},
sender: {
org: 'Security Team',
contact: '[email protected]',
domain: 'example.com'
},
source_identifier: '192.0.2.100',
category: 'messaging',
type: 'spam',
// Custom fields
custom_tracking_id: 'TICKET-12345',
internal_severity_score: 8.5,
automated_response: true
});
Advanced Usage
Async/Await Pattern
Process reports asynchronously:
import { XARFReport } from 'xarf';
async function processReport(jsonData: string): Promise<void> {
try {
const report = XARFReport.fromJSON(jsonData);
if (await report.validate()) {
await submitToAPI(report);
console.log(`Processed report: ${report.report_id}`);
} else {
console.error('Invalid report:', report.validationErrors);
}
} catch (error) {
console.error('Failed to process report:', error);
}
}
Stream Processing
Handle large datasets with streams:
import { XARFStream } from 'xarf';
import { createReadStream } from 'fs';
const stream = new XARFStream();
createReadStream('large-reports.jsonl')
.pipe(stream)
.on('report', async (report) => {
if (await report.validate()) {
await processReport(report);
}
})
.on('error', (error) => {
console.error('Stream error:', error);
})
.on('end', () => {
console.log('Processing complete');
});
Integration Examples
Express.js REST API
import express from 'express';
import { XARFReport, ValidationError } from 'xarf';
const app = express();
app.use(express.json());
app.post('/xarf/submit', async (req, res) => {
try {
// Parse request
const report = XARFReport.fromObject(req.body);
// Validate
if (!await report.validate()) {
return res.status(400).json({
status: 'invalid',
errors: report.validationErrors
});
}
// Process
await processAbuseReport(report);
res.status(202).json({
status: 'accepted',
report_id: report.report_id
});
} catch (error) {
res.status(400).json({
status: 'error',
message: error.message
});
}
});
app.listen(3000);
Next.js API Route
// pages/api/xarf/submit.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { XARFReport } from 'xarf';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const report = XARFReport.fromObject(req.body);
if (!await report.validate()) {
return res.status(400).json({
error: 'Invalid report',
details: report.validationErrors
});
}
await processReport(report);
res.status(202).json({
status: 'accepted',
reportId: report.report_id
});
} catch (error) {
res.status(500).json({ error: error.message });
}
}
React Hook
import { useState, useCallback } from 'react';
import { XARFReport } from 'xarf';
export function useXARFReport() {
const [report, setReport] = useState<XARFReport | null>(null);
const [isValid, setIsValid] = useState(false);
const [errors, setErrors] = useState<string[]>([]);
const validateReport = useCallback(async (data: any) => {
try {
const newReport = XARFReport.fromObject(data);
const valid = await newReport.validate();
setReport(newReport);
setIsValid(valid);
setErrors(valid ? [] : newReport.validationErrors);
return valid;
} catch (error) {
setErrors([error.message]);
return false;
}
}, []);
return { report, isValid, errors, validateReport };
}
Browser Usage
<!DOCTYPE html>
<html>
<head>
<script type="module">
import { XARFReport } from 'https://cdn.jsdelivr.net/npm/xarf/dist/xarf.esm.js';
async function submitReport() {
const report = new XARFReport({
xarf_version: '4.0.0',
report_id: crypto.randomUUID(),
timestamp: new Date().toISOString(),
reporter: {
org: 'Security Team',
contact: '[email protected]',
domain: 'example.com'
},
sender: {
org: 'Security Team',
contact: '[email protected]',
domain: 'example.com'
},
source_identifier: '192.0.2.100',
category: 'messaging',
type: 'spam'
});
if (await report.validate()) {
const response = await fetch('/api/xarf/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: report.toJSON()
});
console.log('Submitted:', await response.json());
}
}
</script>
</head>
<body>
<button onclick="submitReport()">Submit Report</button>
</body>
</html>
Best Practices
1. Always Validate
// ✓ GOOD
const report = XARFReport.fromJSON(data);
if (await report.validate()) {
await processReport(report);
}
// ✗ BAD
const report = XARFReport.fromJSON(data);
await processReport(report); // No validation!
2. Use TypeScript
// ✓ GOOD
async function processReport(report: XARFReport): Promise<void> {
// Type-safe operations
}
// ✗ BAD
async function processReport(report: any) {
// No type safety
}
3. Handle Errors Gracefully
// ✓ GOOD
try {
const report = XARFReport.fromJSON(data);
if (!await report.validate()) {
logger.error('Validation failed', report.validationErrors);
return errorResponse(report.validationErrors);
}
} catch (error) {
logger.error('Parse error', error);
return errorResponse([error.message]);
}
// ✗ BAD
const report = XARFReport.fromJSON(data);
await report.validate(); // Unhandled errors
4. Use Async/Await
// ✓ GOOD
const isValid = await report.validate();
if (isValid) {
await submitReport(report);
}
// ✗ BAD
report.validate().then(isValid => {
if (isValid) {
submitReport(report); // Callback hell
}
});
Resources
- GitHub Repository - Alpha Release
- npm Package - Alpha Release
- Examples - Available
- Issue Tracker - Report bugs
Support
- GitHub Discussions - Ask questions
- Stack Overflow - Tag:
xarf