From 2838f8e1511c36cd9089015c123c2851dbc16377 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 23 Feb 2026 18:25:42 +0100 Subject: [PATCH] Cleanup incorrect case-related backward compatibility layer. --- MIGRATION.md | 64 ++++---- README.md | 91 ++++++------ SCHEMA_EXAMPLES_FIXED.md | 133 ----------------- SCHEMA_EXAMPLES_ISSUES.md | 131 ---------------- TODO.md | 19 +-- docs/FIELD-NAMING.md | 198 ++----------------------- docs/MIGRATION_V3_TO_V4.md | 66 +++++---- examples/snake-case-usage.ts | 31 +--- src/generator.ts | 34 ++--- tests/generator.edge-cases.test.ts | 36 ++--- tests/generator.test.ts | 32 ++-- tests/naming-conventions.test.ts | 196 ------------------------ tests/senior-engineer-feedback.test.ts | 14 +- 13 files changed, 198 insertions(+), 847 deletions(-) delete mode 100644 SCHEMA_EXAMPLES_FIXED.md delete mode 100644 SCHEMA_EXAMPLES_ISSUES.md delete mode 100644 tests/naming-conventions.test.ts diff --git a/MIGRATION.md b/MIGRATION.md index bd1a6a5..61491dc 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -22,6 +22,7 @@ const report = parser.parse(v3JsonData); ### Structure Changes **v3 Format:** + ```json { "Version": "3", @@ -38,6 +39,7 @@ const report = parser.parse(v3JsonData); ``` **v4 Format (after conversion):** + ```json { "xarf_version": "4.0.0", @@ -62,31 +64,31 @@ const report = parser.parse(v3JsonData); ### Field Mappings -| v3 Field | v4 Field | Notes | -|----------|----------|-------| -| `Version` | `xarf_version` | Set to "4.0.0" | -| N/A | `report_id` | Auto-generated UUID | -| `ReporterInfo.ReporterOrg` | `reporter.org` | Direct mapping | -| `ReporterInfo.ReporterOrgEmail` | `reporter.contact` | Direct mapping | -| N/A | `reporter.type` | Set to "manual" for v3 | -| `Report.Date` | `timestamp` | Direct mapping | +| v3 Field | v4 Field | Notes | +| --------------------------------------- | ------------------- | --------------------------- | +| `Version` | `xarf_version` | Set to "4.0.0" | +| N/A | `report_id` | Auto-generated UUID | +| `ReporterInfo.ReporterOrg` | `reporter.org` | Direct mapping | +| `ReporterInfo.ReporterOrgEmail` | `reporter.contact` | Direct mapping | +| N/A | `reporter.type` | Set to "manual" for v3 | +| `Report.Date` | `timestamp` | Direct mapping | | `Report.SourceIp` or `Report.Source.IP` | `source_identifier` | Uses Source.IP if available | -| `Report.ReportType` | `category` + `type` | Mapped per table below | -| `Report.Attachment` or `Report.Samples` | `evidence` | Structure converted | -| N/A | `evidence_source` | Default: "manual_analysis" | +| `Report.ReportType` | `category` + `type` | Mapped per table below | +| `Report.Attachment` or `Report.Samples` | `evidence` | Structure converted | +| N/A | `evidence_source` | Default: "manual_analysis" | ### Report Type Mappings -| v3 ReportType | v4 Category | v4 Type | -|---------------|-------------|---------| -| `Spam` | `messaging` | `spam` | -| `Login-Attack` | `connection` | `login_attack` | -| `Port-Scan` | `connection` | `port_scan` | -| `DDoS` | `connection` | `ddos` | -| `Phishing` | `content` | `phishing` | -| `Malware` | `content` | `malware` | -| `Botnet` | `infrastructure` | `botnet` | -| `Copyright` | `copyright` | `copyright` | +| v3 ReportType | v4 Category | v4 Type | +| -------------- | ---------------- | -------------- | +| `Spam` | `messaging` | `spam` | +| `Login-Attack` | `connection` | `login_attack` | +| `Port-Scan` | `connection` | `port_scan` | +| `DDoS` | `connection` | `ddos` | +| `Phishing` | `content` | `phishing` | +| `Malware` | `content` | `malware` | +| `Botnet` | `infrastructure` | `botnet` | +| `Copyright` | `copyright` | `copyright` | ## Deprecation Warnings @@ -151,10 +153,18 @@ const generator = new XARFGenerator(); const report = generator.generateReport({ category: 'messaging', - reportType: 'spam', - sourceIdentifier: '192.0.2.100', - reporterContact: 'abuse@example.com', - reporterOrg: 'Security Team', + type: 'spam', + source_identifier: '192.0.2.100', + reporter: { + org: 'Security Team', + contact: 'abuse@example.com', + domain: 'example.com', + }, + sender: { + org: 'Security Team', + contact: 'abuse@example.com', + domain: 'example.com', + }, // ... additional fields }); ``` @@ -180,7 +190,7 @@ describe('v3 Migration', () => { expect(v4Report.type).toBeDefined(); // Review any conversion warnings - warnings.forEach(warning => console.log(warning)); + warnings.forEach((warning) => console.log(warning)); }); }); ``` @@ -209,7 +219,7 @@ const v4Report = convertV3toV4(v3Report); v4Report._internal = { ...v4Report._internal, v3_disclosure: v3Report.Disclosure, - v3_contact_name: v3Report.ReporterInfo.ReporterContactName + v3_contact_name: v3Report.ReporterInfo.ReporterContactName, }; ``` diff --git a/README.md b/README.md index 698b07e..70cf836 100644 --- a/README.md +++ b/README.md @@ -44,19 +44,19 @@ const report = parser.parse({ reporter: { org: 'Security Team', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, sender: { org: 'Security Team', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, source_identifier: '192.0.2.100', category: 'connection', type: 'ddos', evidence_source: 'honeypot', destination_ip: '203.0.113.10', - protocol: 'tcp' + protocol: 'tcp', }); console.log(report.category); // 'connection' @@ -70,24 +70,24 @@ import { XARFGenerator } from 'xarf'; const generator = new XARFGenerator(); const report = generator.generateReport({ category: 'messaging', - type: 'spam', // Using XARF spec field name - source_identifier: '192.0.2.100', // snake_case matches XARF spec + type: 'spam', // Using XARF spec field name + source_identifier: '192.0.2.100', // snake_case matches XARF spec reporter: { org: 'Example Security', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, sender: { org: 'Example Security', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, - evidence_source: 'automated_scan', // snake_case matches XARF spec + evidence_source: 'automated_scan', // snake_case matches XARF spec description: 'Spam email detected from source', tags: ['spam', 'email'], // Category-specific fields can be passed directly (union types) protocol: 'smtp', - smtp_from: 'spammer@evil.example.com' + smtp_from: 'spammer@evil.example.com', }); console.log(JSON.stringify(report, null, 2)); @@ -147,7 +147,7 @@ const generator = new XARFGenerator(); #### GeneratorOptions -The `GeneratorOptions` interface for `generateReport()` supports both **snake_case** (XARF spec, preferred) and **camelCase** (backward compatibility). +The `GeneratorOptions` interface uses **snake_case** field names, matching the XARF specification. **Union Types**: Category-specific fields can be passed directly without using `additionalFields`: @@ -163,7 +163,7 @@ const messagingReport = generator.generateReport({ // Messaging-specific fields directly on options protocol: 'smtp', smtp_from: 'spammer@evil.example.com', - subject: 'You won!' + subject: 'You won!', }); // Connection report with direct fields @@ -177,32 +177,28 @@ const connectionReport = generator.generateReport({ // Connection-specific fields directly on options destination_ip: '203.0.113.10', protocol: 'tcp', - destination_port: 80 + destination_port: 80, }); ``` **Base Options** (all categories): + ```typescript { category: XARFCategory; // Required: Report category type?: string; // Report type (e.g., 'spam', 'ddos') - reportType?: string; // Backward compatibility alias source_identifier?: string; // Required: Source IP or identifier - sourceIdentifier?: string; // Backward compatibility alias reporter: ContactInfo; // Required: Reporter information sender: ContactInfo; // Required: Sender information evidence_source?: EvidenceSource; // Evidence source - evidenceSource?: EvidenceSource; // Backward compatibility alias description?: string; // Human-readable description evidence?: XARFEvidence[]; // Evidence items confidence?: number; // Confidence score (0.0 to 1.0) tags?: string[]; // Tags for categorization - additionalFields?: Record; // Additional fields (legacy approach) + additionalFields?: Record; // Additional fields } ``` -**Note:** The library accepts both naming conventions but generates reports using snake_case as per the XARF specification. - ### XARFValidator Comprehensive validation with detailed error and warning reporting. @@ -216,11 +212,13 @@ const validator = new XARFValidator(); - `validate(report: XARFReport, strict?: boolean, showMissingOptional?: boolean): ValidationResult` - Validate a report Parameters: + - `report` - The XARF report to validate - `strict` - If `true`, throw `XARFValidationError` on validation failures (default: `false`) - `showMissingOptional` - If `true`, include info about missing optional fields (default: `false`) Returns: + ```typescript { valid: boolean; @@ -237,7 +235,7 @@ The validator automatically warns about unknown fields in reports: ```typescript const report = { // ... valid fields ... - unknownField: 'value' // Will trigger a warning + unknownField: 'value', // Will trigger a warning }; const result = validator.validate(report); @@ -292,24 +290,31 @@ const metadata = schemaRegistry.getFieldMetadata('confidence'); ## Categories and Types ### Messaging + - `spam`, `phishing`, `social_engineering`, `bulk_messaging` ### Connection + - `ddos`, `port_scan`, `login_attack`, `ip_spoofing`, `compromised`, `botnet`, `malicious_traffic`, and more ### Content + - `phishing_site`, `malware_distribution`, `defacement`, `spamvertised`, `web_hack`, and more ### Infrastructure + - `botnet`, `compromised_server` ### Copyright + - `infringement`, `dmca`, `trademark`, `p2p`, and more ### Vulnerability + - `cve`, `misconfiguration`, `open_service` ### Reputation + - `blocklist`, `threat_intelligence` ## Examples @@ -324,12 +329,12 @@ const report = generator.generateReport({ reporter: { org: 'Security Operations', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, sender: { org: 'Security Operations', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, evidence_source: 'honeypot', // Category-specific fields directly (union types) @@ -337,7 +342,7 @@ const report = generator.generateReport({ protocol: 'tcp', destination_port: 80, attack_type: 'syn_flood', - confidence: 0.95 + confidence: 0.95, }); ``` @@ -351,19 +356,19 @@ const report = generator.generateReport({ reporter: { org: 'Phishing Response Team', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, sender: { org: 'Phishing Response Team', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, evidence_source: 'user_report', // Category-specific fields directly (union types) url: 'http://phishing.example.com', content_type: 'text/html', description: 'Phishing site mimicking banking portal', - tags: ['phishing', 'banking', 'credential-theft'] + tags: ['phishing', 'banking', 'credential-theft'], }); ``` @@ -377,12 +382,12 @@ const report = generator.generateReport({ reporter: { org: 'Example Security', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, sender: { org: 'Example Security', contact: 'abuse@example.com', - domain: 'example.com' + domain: 'example.com', }, evidence_source: 'spamtrap', // Category-specific fields directly (union types) @@ -390,7 +395,7 @@ const report = generator.generateReport({ smtp_from: 'spammer@evil.example.com', smtp_to: 'victim@example.com', subject: 'You won the lottery!', - message_id: '<123456@evil.example.com>' + message_id: '<123456@evil.example.com>', }); ``` @@ -404,7 +409,7 @@ import type { ConnectionReport, MessagingReport, XARFCategory, - ReporterType + ReporterType, } from 'xarf'; const report: ConnectionReport = { @@ -450,6 +455,7 @@ npm run check-schema-updates -- --all ``` Example output: + ``` [xarf] Checking for schema updates... @@ -465,6 +471,7 @@ Example output: To update to a newer version of the XARF specification: 1. Edit `package.json` and update the version: + ```json "xarfSpec": { "version": "v4.2.0" @@ -535,15 +542,15 @@ const v3Report = { Version: '3', ReporterInfo: { ReporterOrg: 'Security Team', - ReporterOrgEmail: 'abuse@example.com' + ReporterOrgEmail: 'abuse@example.com', }, Report: { ReportType: 'Spam', Date: '2024-01-15T10:00:00Z', SourceIp: '192.0.2.100', Protocol: 'smtp', - SmtpMailFromAddress: 'spammer@evil.example' - } + SmtpMailFromAddress: 'spammer@evil.example', + }, }; // Automatically converted to v4 format @@ -588,16 +595,16 @@ console.log(getV3DeprecationWarning()); The following v3 report types are automatically mapped to v4 categories: -| v3 ReportType | v4 Category | v4 Type | -|---------------|-------------|---------| -| Spam | messaging | spam | -| Login-Attack | connection | login_attack | -| Port-Scan | connection | port_scan | -| DDoS | connection | ddos | -| Phishing | content | phishing | -| Malware | content | malware | -| Botnet | infrastructure | botnet | -| Copyright | copyright | copyright | +| v3 ReportType | v4 Category | v4 Type | +| ------------- | -------------- | ------------ | +| Spam | messaging | spam | +| Login-Attack | connection | login_attack | +| Port-Scan | connection | port_scan | +| DDoS | connection | ddos | +| Phishing | content | phishing | +| Malware | content | malware | +| Botnet | infrastructure | botnet | +| Copyright | copyright | copyright | Unknown v3 report types are mapped to category `content` with type `unclassified`. diff --git a/SCHEMA_EXAMPLES_FIXED.md b/SCHEMA_EXAMPLES_FIXED.md deleted file mode 100644 index 227c9a8..0000000 --- a/SCHEMA_EXAMPLES_FIXED.md +++ /dev/null @@ -1,133 +0,0 @@ -# JSON Schema Examples - FIXED! ✅ - -## Summary -**ALL 34 examples now validate correctly at 100%** against XARF v4.0.0 specification! 🎉 - -**Validation Results**: -- Total examples tested: 34 -- Passed: 34 (100%) ✅✅✅ -- Failed: 0 ❌ -- Success rate: **100.0%** - -## What Was Fixed - -All 30 examples in `xarf-spec/schemas/v4/types/*.json` have been corrected to include required v4.0.0 fields: - -### 1. Added `sender` Field (ALL 30 examples) ✅ -**XARF v4.0.0 Spec**: `sender` is a required field with ContactInfo structure -```json -"sender": { - "org": "Example Security", - "contact": "abuse@example.com", - "domain": "example.com" -} -``` - -### 2. Fixed `reporter` Object (ALL 30 examples) ✅ -- Added required `domain` field -- Removed invalid `type` field (not part of ContactInfo schema) - -```json -// ✅ CORRECT -"reporter": { - "org": "DDoS Protection Service", - "contact": "ddos@protectionservice.net", - "domain": "protectionservice.net" -} -``` - -### 3. Fixed `report_id` Format (ALL 30 examples) ✅ -Changed from prefixed strings to valid UUID v4 format - -```json -// ✅ CORRECT -"report_id": "789a0123-b456-48c9-a012-345678901234" -``` - -### 4. Fixed `evidence_source` Values (ALL 30 examples) ✅ -Updated to use only allowed values from core schema: -- spamtrap, user_complaint, automated_filter, honeypot, crawler -- user_report, automated_scan, spam_analysis, firewall_logs -- ids_detection, flow_analysis, vulnerability_scan, researcher_analysis -- automated_discovery, traffic_analysis, threat_intelligence - -## Files Fixed (30 Total) - -### Connection Types (6 files) ✅ -- connection-ddos.json -- connection-infected-host.json -- connection-reconnaissance.json -- connection-scraping.json -- connection-sql-injection.json -- connection-vulnerability-scan.json - -### Content Types (9 files) ✅ -- content-brand_infringement.json -- content-csam.json -- content-csem.json -- content-exposed-data.json -- content-fraud.json -- content-malware.json -- content-phishing.json -- content-remote_compromise.json -- content-suspicious_registration.json - -### Copyright Types (6 files) ✅ -- copyright-copyright.json -- copyright-cyberlocker.json -- copyright-link-site.json -- copyright-p2p.json -- copyright-ugc-platform.json -- copyright-usenet.json - -### Infrastructure Types (2 files) ✅ -- infrastructure-botnet.json -- infrastructure-compromised-server.json - -### Messaging Types (2 files) ✅ -- messaging-bulk-messaging.json -- messaging-spam.json - -### Reputation Types (2 files) ✅ -- reputation-blocklist.json -- reputation-threat-intelligence.json - -### Vulnerability Types (3 files) ✅ -- vulnerability-cve.json -- vulnerability-misconfiguration.json -- vulnerability-open-service.json - -## Impact - -1. ✅ **Users can now copy spec examples to create valid reports** -2. ✅ **Validators implementing against these examples will be correct** -3. ✅ **Spec compliance can be verified** using the spec's own examples -4. ✅ **All examples follow ContactInfo structure requirements** -5. ✅ **All examples use valid UUID formats** -6. ✅ **All examples use allowed evidence_source values** - -## Testing - -Validation performed using: -- xarf-javascript v1.0.0 (implements XARF v4.0.0 spec) -- AJV JSON Schema validator -- Official xarf-spec JSON schemas -- Hand-coded validator with expanded evidence_source list - -Test script: `validate-all-examples.js` -Run with: `node validate-all-examples.js` - -## Changes Applied by AI Swarm - -All fixes were applied using coordinated AI swarm with 30+ specialized agents: -- Each agent handled individual schema files with context awareness -- Changes were reviewed file-by-file, not blindly applied -- Agents identified when changes would break functionality -- Human-like review process caught edge cases - -## Date Fixed -December 16, 2025 - -## Fixed By -AI Swarm Coordination (30+ specialized worker agents) -Orchestrated via Claude Code + claude-flow MCP integration diff --git a/SCHEMA_EXAMPLES_ISSUES.md b/SCHEMA_EXAMPLES_ISSUES.md deleted file mode 100644 index 95314a0..0000000 --- a/SCHEMA_EXAMPLES_ISSUES.md +++ /dev/null @@ -1,131 +0,0 @@ -# JSON Schema Examples Validation Issues - -## Summary -**93.7% of examples in xarf-spec JSON schemas fail validation** against XARF v4.0.0 specification. - -**Validation Results**: -- Total examples tested: 32 -- Passed: 2 (6.3%) - Only our library's README examples -- Failed: 30 (93.7%) - All xarf-spec schema examples - -## Critical Issues - -All 30 examples in `xarf-spec/schemas/v4/types/*.json` are missing required v4.0.0 fields: - -### 1. Missing `sender` Field (ALL 30 examples) -**XARF v4.0.0 Spec**: `sender` is a required field -```json -// ❌ WRONG (all current examples) -{ - "xarf_version": "4.0.0", - "reporter": { ... } - // Missing sender field! -} - -// ✅ CORRECT -{ - "xarf_version": "4.0.0", - "reporter": { - "org": "Example Security", - "contact": "abuse@example.com", - "domain": "example.com" - }, - "sender": { - "org": "Example Security", - "contact": "abuse@example.com", - "domain": "example.com" - } -} -``` - -### 2. Incomplete `reporter` Object (ALL 30 examples) -**Issue**: Missing required `domain` field, contains invalid `type` field - -```json -// ❌ WRONG -"reporter": { - "org": "DDoS Protection Service", - "contact": "ddos@protectionservice.net", - "type": "automated" // Wrong - 'type' is not a ContactInfo field -} - -// ✅ CORRECT -"reporter": { - "org": "DDoS Protection Service", - "contact": "ddos@protectionservice.net", - "domain": "protectionservice.net" -} -``` - -### 3. Invalid `report_id` Format (ALL 30 examples) -**Issue**: Examples use non-UUID strings - -```json -// ❌ WRONG -"report_id": "ddos-789a0123-b456-78c9-d012-345678901234" // Not valid UUID format - -// ✅ CORRECT -"report_id": "789a0123-b456-48c9-a012-345678901234" // Valid UUID v4 -``` - -### 4. Missing `evidence_source` (Many examples) -**Issue**: Required field not present in several examples - -## Affected Files - -All type-specific schema files in `xarf-spec/schemas/v4/types/`: -- connection-ddos.json ❌ -- connection-infected-host.json ❌ -- connection-reconnaissance.json ❌ -- connection-scraping.json ❌ -- connection-sql-injection.json ❌ -- connection-vulnerability-scan.json ❌ -- content-brand_infringement.json ❌ -- content-csam.json ❌ -- content-csem.json ❌ -- content-exposed-data.json ❌ -- content-fraud.json ❌ -- content-malware.json ❌ -- content-phishing.json ❌ -- content-remote_compromise.json ❌ -- content-suspicious_registration.json ❌ -- copyright-copyright.json ❌ -- copyright-cyberlocker.json ❌ -- copyright-link-site.json ❌ -- copyright-p2p.json ❌ -- copyright-ugc-platform.json ❌ -- copyright-usenet.json ❌ -- infrastructure-botnet.json ❌ -- infrastructure-compromised-server.json ❌ -- messaging-bulk-messaging.json ❌ -- messaging-spam.json ❌ -- reputation-blocklist.json ❌ -- reputation-threat-intelligence.json ❌ -- vulnerability-cve.json ❌ -- vulnerability-misconfiguration.json ❌ -- vulnerability-open-service.json ❌ - -## Impact - -1. **Users copying spec examples will create invalid reports** -2. **Validators implementing against these examples will be incorrect** -3. **Spec compliance cannot be verified** using the spec's own examples - -## Recommended Fix - -Update all examples in xarf-spec to include: -1. Required `sender` field (matching ContactInfo structure) -2. Complete `reporter` object with `domain` field (remove invalid `type` field) -3. Valid UUID format for `report_id` -4. Required `evidence_source` field - -## Testing - -Validation performed using: -- xarf-javascript v1.0.0 (implements XARF v4.0.0 spec) -- AJV JSON Schema validator -- Official xarf-spec JSON schemas - -Test script available at: `validate-all-examples.js` - -Run with: `node validate-all-examples.js` diff --git a/TODO.md b/TODO.md index 451f59c..5631def 100644 --- a/TODO.md +++ b/TODO.md @@ -3,9 +3,11 @@ ## High Priority ### JSON Schema Validation - Production Ready + **Status:** Core schema validation ✅ PRODUCTION READY | Type-specific validation ⚠️ KNOWN LIMITATION **What Works:** + - ✅ Core schema validation (`SchemaValidator.validateCore()`) - ✅ All 207 tests passing (46 schema-specific tests) - ✅ Format validation (email, UUID, ISO dates, hostnames) @@ -20,6 +22,7 @@ - ✅ Schema infrastructure fully implemented **Known Limitation:** + - ⚠️ Type-specific required fields not enforced by master schema validation - This is due to a design flaw in the XARF spec's master schema structure - The master schema uses `anyOf` with `if/then` which has a logical issue: @@ -29,6 +32,7 @@ **Recommended Fix (for XARF spec team):** Restructure master schema from: + ```json { "anyOf": [ { "if": { ... }, "then": { "$ref": "types/..." } } @@ -36,6 +40,7 @@ Restructure master schema from: ``` To: + ```json { "oneOf": [ { "allOf": [ @@ -46,21 +51,17 @@ To: ``` **Current Recommendation:** + - Use `SchemaValidator.validateCore()` for production validation - Core schema provides excellent coverage of XARF v4.0.0 spec - Type-specific validation requires hand-coded validator or spec master schema fix ## Completed -### snake_case vs camelCase API (Fixed 2025-12-16) -- ✅ Generator now accepts both `type` and `reportType` -- ✅ Generator now accepts both `source_identifier` and `sourceIdentifier` -- ✅ Generator now accepts both `evidence_source` and `evidenceSource` -- ✅ Generator now accepts both `on_behalf_of` and `onBehalfOf` -- ✅ Generated reports use snake_case as per XARF spec -- ✅ README updated to show snake_case examples (preferred) -- ✅ API documentation clearly marks camelCase as deprecated -- ✅ Backward compatibility maintained +### snake_case API (Fixed 2025-12-16, camelCase compat removed) + +- ✅ Generator uses snake_case field names matching the XARF spec +- ✅ camelCase backward compatibility removed (was never in the spec) ## Low Priority diff --git a/docs/FIELD-NAMING.md b/docs/FIELD-NAMING.md index 697d810..7c52f60 100644 --- a/docs/FIELD-NAMING.md +++ b/docs/FIELD-NAMING.md @@ -1,14 +1,10 @@ # Field Naming Conventions -## Overview +## snake_case Throughout -The XARF JavaScript library now supports both **snake_case** (XARF specification format) and **camelCase** (backward compatibility) field names in the `GeneratorOptions` interface. +The XARF v4.0.0 specification uses **snake_case** for all field names, and this library follows that convention in both input and output. -## Preferred Format: snake_case - -The XARF v4.0.0 specification uses **snake_case** for all field names. This is the preferred format and should be used in all new code. - -### Example (Preferred) +### Example ```typescript import { XARFGenerator } from '@xarf/xarf-javascript'; @@ -17,14 +13,9 @@ const generator = new XARFGenerator(); const report = generator.generateReport({ category: 'connection', - type: 'ddos', // XARF spec field name - source_identifier: '192.0.2.100', // XARF spec field name - evidence_source: 'honeypot', // XARF spec field name - on_behalf_of: { // XARF spec field name - org: 'Client Company', - contact: 'abuse@client.com', - domain: 'client.com', - }, + type: 'ddos', + source_identifier: '192.0.2.100', + evidence_source: 'honeypot', reporter: { org: 'Security Team', contact: 'security@example.com', @@ -35,189 +26,26 @@ const report = generator.generateReport({ contact: 'soc@example.com', domain: 'example.com', }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - }, -}); -``` - -## Backward Compatibility: camelCase - -For backward compatibility, the library still accepts **camelCase** field names. However, these are **deprecated** and may be removed in a future major version. - -### Example (Deprecated) - -```typescript -const report = generator.generateReport({ - category: 'connection', - reportType: 'ddos', // Deprecated: use "type" - sourceIdentifier: '192.0.2.100', // Deprecated: use "source_identifier" - evidenceSource: 'honeypot', // Deprecated: use "evidence_source" - onBehalfOf: { // Deprecated: use "on_behalf_of" - org: 'Client Company', - contact: 'abuse@client.com', - domain: 'client.com', - }, - reporter: { /* ... */ }, - sender: { /* ... */ }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - }, -}); -``` - -## Field Name Mapping - -| XARF Spec (snake_case) | Deprecated (camelCase) | Required | -|------------------------|------------------------|----------| -| `type` | `reportType` | Yes | -| `source_identifier` | `sourceIdentifier` | Yes | -| `evidence_source` | `evidenceSource` | No | -| `on_behalf_of` | `onBehalfOf` | No | - -## Precedence Rules - -When both naming conventions are provided, **snake_case takes precedence**: - -```typescript -const report = generator.generateReport({ - category: 'connection', - type: 'port_scan', // ✓ This value is used - reportType: 'ddos', // ✗ This value is ignored - source_identifier: '192.0.2.50', // ✓ This value is used - sourceIdentifier: '192.0.2.100', // ✗ This value is ignored - // ... -}); - -// Result: report.type === 'port_scan' -``` - -## Output Format - -**All generated reports use snake_case field names**, regardless of the input format. This ensures compliance with the XARF v4.0.0 specification. - -```typescript -// Input: camelCase -const report = generator.generateReport({ - reportType: 'ddos', - sourceIdentifier: '192.0.2.100', - // ... + destination_ip: '203.0.113.50', + protocol: 'tcp', }); - -// Output: ALWAYS snake_case -console.log(report.type); // 'ddos' -console.log(report.source_identifier); // '192.0.2.100' -console.log(report.reportType); // undefined -console.log(report.sourceIdentifier); // undefined -``` - -## Migration Guide - -### Step 1: Update Generator Calls - -Replace camelCase field names with snake_case: - -```typescript -// Before (deprecated) -generator.generateReport({ - category: 'messaging', - reportType: 'spam', - sourceIdentifier: '192.0.2.100', - evidenceSource: 'spamtrap', - onBehalfOf: { /* ... */ }, - // ... -}); - -// After (preferred) -generator.generateReport({ - category: 'messaging', - type: 'spam', - source_identifier: '192.0.2.100', - evidence_source: 'spamtrap', - on_behalf_of: { /* ... */ }, - // ... -}); -``` - -### Step 2: Update Type Annotations - -If you're using TypeScript with explicit types, update your interfaces: - -```typescript -// Before -interface MyReportOptions { - reportType: string; - sourceIdentifier: string; - evidenceSource?: string; -} - -// After -interface MyReportOptions { - type: string; - source_identifier: string; - evidence_source?: string; -} -``` - -### Step 3: Verify Tests - -Ensure your tests check for snake_case field names in the output: - -```typescript -expect(report.type).toBe('ddos'); -expect(report.source_identifier).toBe('192.0.2.100'); -expect(report.evidence_source).toBe('honeypot'); ``` -## Error Messages +### Additional Fields -Error messages reference both naming conventions for clarity: - -```typescript -try { - generator.generateReport({ - category: 'connection', - // Missing type/reportType - reporter: { /* ... */ }, - sender: { /* ... */ }, - }); -} catch (error) { - console.error(error.message); - // "type (or reportType) is required" -} -``` - -## Additional Fields - -The `additionalFields` object should always use snake_case field names to match the XARF specification: +The `additionalFields` object should also use snake_case field names: ```typescript generator.generateReport({ // ... additionalFields: { - destination_ip: '203.0.113.50', // ✓ Correct - destination_port: 80, // ✓ Correct - packet_count: 1500, // ✓ Correct - // destinationIp: '203.0.113.50', // ✗ Incorrect + destination_ip: '203.0.113.50', + destination_port: 80, + packet_count: 1500, }, }); ``` -## Benefits of snake_case - -1. **Spec Compliance**: Matches XARF v4.0.0 specification exactly -2. **Consistency**: Same naming in code and JSON output -3. **Interoperability**: Compatible with other XARF implementations -4. **Clarity**: No need to mentally map between input and output formats - -## Deprecation Timeline - -- **v1.x**: Both snake_case and camelCase supported (current) -- **v2.x**: Both supported, deprecation warnings added -- **v3.x**: camelCase removed, only snake_case supported - ## See Also - [XARF v4.0.0 Specification](https://xarf.org/spec/) diff --git a/docs/MIGRATION_V3_TO_V4.md b/docs/MIGRATION_V3_TO_V4.md index 8b9da38..c0deaf0 100644 --- a/docs/MIGRATION_V3_TO_V4.md +++ b/docs/MIGRATION_V3_TO_V4.md @@ -22,6 +22,7 @@ const report = parser.parse(v3JsonData); ### Structure Changes **v3 Format:** + ```json { "Version": "3", @@ -38,6 +39,7 @@ const report = parser.parse(v3JsonData); ``` **v4 Format (after conversion):** + ```json { "xarf_version": "4.0.0", @@ -62,32 +64,32 @@ const report = parser.parse(v3JsonData); ### Field Mappings -| v3 Field | v4 Field | Notes | -|----------|----------|-------| -| `Version` | `xarf_version` | Set to "4.0.0" | -| N/A | `report_id` | Auto-generated UUID | -| `ReporterInfo.ReporterOrg` | `reporter.org` | Direct mapping | -| `ReporterInfo.ReporterOrgEmail` | `reporter.contact` | Direct mapping | -| N/A | `reporter.type` | Set to "manual" for v3 | -| `Report.Date` | `timestamp` | Direct mapping | +| v3 Field | v4 Field | Notes | +| --------------------------------------- | ------------------- | --------------------------- | +| `Version` | `xarf_version` | Set to "4.0.0" | +| N/A | `report_id` | Auto-generated UUID | +| `ReporterInfo.ReporterOrg` | `reporter.org` | Direct mapping | +| `ReporterInfo.ReporterOrgEmail` | `reporter.contact` | Direct mapping | +| N/A | `reporter.type` | Set to "manual" for v3 | +| `Report.Date` | `timestamp` | Direct mapping | | `Report.SourceIp` or `Report.Source.IP` | `source_identifier` | Uses Source.IP if available | -| `Report.ReportType` | `category` + `type` | Mapped per table below | -| `Report.Attachment` or `Report.Samples` | `evidence` | Structure converted | -| N/A | `evidence_source` | Default: "manual_analysis" | +| `Report.ReportType` | `category` + `type` | Mapped per table below | +| `Report.Attachment` or `Report.Samples` | `evidence` | Structure converted | +| N/A | `evidence_source` | Default: "manual_analysis" | ### Report Type Mappings -| v3 ReportType | v4 Category | v4 Type | -|---------------|-------------|---------| -| `Spam` | `messaging` | `spam` | -| `Login-Attack` | `connection` | `login_attack` | -| `Port-Scan` | `connection` | `port_scan` | -| `DDoS` | `connection` | `ddos` | -| `Phishing` | `content` | `phishing` | -| `Malware` | `content` | `malware` | -| `Botnet` | `infrastructure` | `botnet` | -| `Copyright` | `copyright` | `copyright` | -| Unknown types | `content` | `unclassified` | +| v3 ReportType | v4 Category | v4 Type | +| -------------- | ---------------- | -------------- | +| `Spam` | `messaging` | `spam` | +| `Login-Attack` | `connection` | `login_attack` | +| `Port-Scan` | `connection` | `port_scan` | +| `DDoS` | `connection` | `ddos` | +| `Phishing` | `content` | `phishing` | +| `Malware` | `content` | `malware` | +| `Botnet` | `infrastructure` | `botnet` | +| `Copyright` | `copyright` | `copyright` | +| Unknown types | `content` | `unclassified` | **Note**: XARF v4 has exactly 7 categories. Unknown v3 report types are mapped to the `content` category with type `unclassified`. @@ -154,10 +156,18 @@ const generator = new XARFGenerator(); const report = generator.generateReport({ category: 'messaging', - reportType: 'spam', - sourceIdentifier: '192.0.2.100', - reporterContact: 'abuse@example.com', - reporterOrg: 'Security Team', + type: 'spam', + source_identifier: '192.0.2.100', + reporter: { + org: 'Security Team', + contact: 'abuse@example.com', + domain: 'example.com', + }, + sender: { + org: 'Security Team', + contact: 'abuse@example.com', + domain: 'example.com', + }, // ... additional fields }); ``` @@ -183,7 +193,7 @@ describe('v3 Migration', () => { expect(v4Report.type).toBeDefined(); // Review any conversion warnings - warnings.forEach(warning => console.log(warning)); + warnings.forEach((warning) => console.log(warning)); }); }); ``` @@ -212,7 +222,7 @@ const v4Report = convertV3toV4(v3Report); v4Report._internal = { ...v4Report._internal, v3_disclosure: v3Report.Disclosure, - v3_contact_name: v3Report.ReporterInfo.ReporterContactName + v3_contact_name: v3Report.ReporterInfo.ReporterContactName, }; ``` diff --git a/examples/snake-case-usage.ts b/examples/snake-case-usage.ts index 29dd656..6e38e25 100644 --- a/examples/snake-case-usage.ts +++ b/examples/snake-case-usage.ts @@ -86,33 +86,4 @@ const report2 = generator.generateReport({ console.log(JSON.stringify(report2, null, 2)); console.log('\n---\n'); -// Example 3: Backward compatibility with camelCase (deprecated but still works) -console.log('Example 3: Backward compatibility with camelCase (deprecated)'); -const report3 = generator.generateReport({ - category: 'content', - reportType: 'malware_distribution', // Deprecated: use "type" instead - sourceIdentifier: '203.0.113.75', // Deprecated: use "source_identifier" instead - evidenceSource: 'automated_scan', // Deprecated: use "evidence_source" instead - reporter: { - org: 'Threat Intelligence Team', - contact: 'threats@security.example.com', - domain: 'security.example.com', - }, - sender: { - org: 'Automated Scanning System', - contact: 'scanner@security.example.com', - domain: 'security.example.com', - }, - description: 'Malware distribution site detected', - additionalFields: { - url: 'http://malicious.example.com/download.exe', - content_type: 'application/octet-stream', - }, -}); - -console.log(JSON.stringify(report3, null, 2)); -console.log('\n---\n'); - -console.log('Note: All generated reports use snake_case in the output,'); -console.log('regardless of whether you use snake_case or camelCase in the input.'); -console.log('Using snake_case is preferred as it matches the XARF specification.'); +console.log('Note: All field names use snake_case, matching the XARF specification.'); diff --git a/src/generator.ts b/src/generator.ts index 570c219..62739da 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -21,18 +21,10 @@ import type { /** * Base generator options shared by all categories - * - * Supports both camelCase (backward compatibility) and snake_case (XARF spec) field names. - * Snake_case is preferred and matches the XARF specification. */ export interface BaseGeneratorOptions { - // Type field (XARF spec uses "type") - type?: string; // XARF spec field name - reportType?: string; // Backward compatibility (deprecated) - - // Source identifier - source_identifier?: string; // XARF spec field name - sourceIdentifier?: string; // Backward compatibility (deprecated) + type?: string; + source_identifier?: string; reporter: { org: string; @@ -45,16 +37,13 @@ export interface BaseGeneratorOptions { domain: string; }; - // Evidence source - evidence_source?: EvidenceSource; // XARF spec field name - evidenceSource?: EvidenceSource; // Backward compatibility (deprecated) + evidence_source?: EvidenceSource; description?: string; evidence?: XARFEvidence[]; confidence?: number; tags?: string[]; - // Additional fields for backward compatibility and extra fields additionalFields?: Record; } @@ -202,8 +191,7 @@ export interface ReputationGeneratorOptions extends BaseGeneratorOptions { /** * Discriminated union type for all generator options * - * Provides type-safe, category-specific fields while maintaining - * backward compatibility through additionalFields. + * Provides type-safe, category-specific fields for each report category. */ export type GeneratorOptions = | ContentGeneratorOptions @@ -358,10 +346,9 @@ export class XARFGenerator { additionalFields, } = options; - // Normalize field names: prefer snake_case (XARF spec), fall back to camelCase (backward compat) - const reportType = options.type ?? options.reportType; - const sourceIdentifier = options.source_identifier ?? options.sourceIdentifier; - const evidenceSource = options.evidence_source ?? options.evidenceSource; + const reportType = options.type; + const sourceIdentifier = options.source_identifier; + const evidenceSource = options.evidence_source; // Validate all required fields this.validateRequiredOptions(sourceIdentifier, reportType, reporter, sender); @@ -432,11 +419,8 @@ export class XARFGenerator { 'tags', 'additionalFields', 'type', - 'reportType', 'source_identifier', - 'sourceIdentifier', 'evidence_source', - 'evidenceSource', ]); /** @@ -486,10 +470,10 @@ export class XARFGenerator { sender: { org: string; contact: string; domain: string } | undefined ): void { if (!sourceIdentifier) { - throw new XARFError('source_identifier (or sourceIdentifier) is required'); + throw new XARFError('source_identifier is required'); } if (!reportType) { - throw new XARFError('type (or reportType) is required'); + throw new XARFError('type is required'); } if (!reporter) { throw new XARFError('reporter is required'); diff --git a/tests/generator.edge-cases.test.ts b/tests/generator.edge-cases.test.ts index 3ee2865..39cb2b0 100644 --- a/tests/generator.edge-cases.test.ts +++ b/tests/generator.edge-cases.test.ts @@ -17,8 +17,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: null as any, sender: { org: 'Example Org', @@ -30,8 +30,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: null as any, sender: { org: 'Example Org', @@ -46,8 +46,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'invalid-email', @@ -63,8 +63,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'invalid-email', @@ -83,8 +83,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -95,14 +95,14 @@ describe('XARFGenerator Edge Cases', () => { contact: 'abuse@example.com', domain: 'example.com', }, - evidenceSource: 'invalid_source' as any, + evidence_source: 'invalid_source' as any, }); }).toThrow(XARFError); expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -113,7 +113,7 @@ describe('XARFGenerator Edge Cases', () => { contact: 'abuse@example.com', domain: 'example.com', }, - evidenceSource: 'invalid_source' as any, + evidence_source: 'invalid_source' as any, }); }).toThrow('Invalid evidence_source'); }); @@ -122,8 +122,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -140,8 +140,8 @@ describe('XARFGenerator Edge Cases', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', diff --git a/tests/generator.test.ts b/tests/generator.test.ts index 5dd62fd..04a009c 100644 --- a/tests/generator.test.ts +++ b/tests/generator.test.ts @@ -94,8 +94,8 @@ describe('XARFGenerator', () => { it('should generate valid connection report', () => { const report = generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.100', + type: 'ddos', + source_identifier: '192.0.2.100', reporter: { org: 'Example Security', contact: 'abuse@example.com', @@ -132,8 +132,8 @@ describe('XARFGenerator', () => { const evidence = generator.addEvidence('text/plain', 'Test', 'data'); const report = generator.generateReport({ category: 'content', - reportType: 'phishing', - sourceIdentifier: '192.0.2.100', + type: 'phishing', + source_identifier: '192.0.2.100', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -162,8 +162,8 @@ describe('XARFGenerator', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '', + type: 'ddos', + source_identifier: '', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -182,8 +182,8 @@ describe('XARFGenerator', () => { expect(() => { generator.generateReport({ category: 'invalid' as any, - reportType: 'test', - sourceIdentifier: '192.0.2.1', + type: 'test', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -202,8 +202,8 @@ describe('XARFGenerator', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'spam', - sourceIdentifier: '192.0.2.1', + type: 'spam', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -222,8 +222,8 @@ describe('XARFGenerator', () => { expect(() => { generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.1', + type: 'ddos', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -239,12 +239,12 @@ describe('XARFGenerator', () => { }).toThrow(XARFError); }); - it('should throw error for invalid on_behalf_of', () => { + it('should throw error for unknown fields', () => { expect(() => { generator.generateReport({ category: 'messaging', - reportType: 'spam', - sourceIdentifier: '192.0.2.1', + type: 'spam', + source_identifier: '192.0.2.1', reporter: { org: 'Example Org', contact: 'abuse@example.com', @@ -255,7 +255,7 @@ describe('XARFGenerator', () => { contact: 'abuse@example.com', domain: 'example.com', }, - onBehalfOf: { org: 'Test' } as any, + additionalFields: { unknown_field: 'test' }, }); }).toThrow(XARFError); }); diff --git a/tests/naming-conventions.test.ts b/tests/naming-conventions.test.ts deleted file mode 100644 index afc32fc..0000000 --- a/tests/naming-conventions.test.ts +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Tests for snake_case vs camelCase field naming support - */ - -import { XARFGenerator } from '../src/generator'; - -describe('Field Naming Convention Support', () => { - let generator: XARFGenerator; - - beforeEach(() => { - generator = new XARFGenerator(); - }); - - describe('snake_case (XARF spec preferred)', () => { - it('should accept snake_case field names', () => { - const report = generator.generateReport({ - category: 'connection', - type: 'ddos', // XARF spec field name - source_identifier: '192.0.2.100', // XARF spec field name - evidence_source: 'honeypot', // Valid for connection/ddos schema - reporter: { - org: 'Security Team', - contact: 'security@example.com', - domain: 'example.com', - }, - sender: { - org: 'SOC', - contact: 'soc@example.com', - domain: 'example.com', - }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - first_seen: '2024-01-15T09:00:00Z', - source_port: 12345, - }, - }); - - expect(report.type).toBe('ddos'); - expect(report.source_identifier).toBe('192.0.2.100'); - expect(report.evidence_source).toBe('honeypot'); - }); - }); - - describe('camelCase (backward compatibility)', () => { - it('should accept camelCase field names for backward compatibility', () => { - const report = generator.generateReport({ - category: 'connection', - reportType: 'ddos', // Backward compatibility - sourceIdentifier: '192.0.2.100', // Backward compatibility - evidenceSource: 'honeypot', // Valid for connection/ddos - reporter: { - org: 'Security Team', - contact: 'security@example.com', - domain: 'example.com', - }, - sender: { - org: 'SOC', - contact: 'soc@example.com', - domain: 'example.com', - }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - first_seen: '2024-01-15T09:00:00Z', - source_port: 12345, - }, - }); - - // Output should ALWAYS use snake_case (XARF spec) - expect(report.type).toBe('ddos'); - expect(report.source_identifier).toBe('192.0.2.100'); - expect(report.evidence_source).toBe('honeypot'); - }); - }); - - describe('snake_case takes precedence', () => { - it('should prefer snake_case when both are provided', () => { - const report = generator.generateReport({ - category: 'connection', - type: 'port_scan', // XARF spec - should be used - reportType: 'ddos', // Backward compat - should be ignored - source_identifier: '192.0.2.50', // XARF spec - should be used - sourceIdentifier: '192.0.2.100', // Backward compat - should be ignored - evidence_source: 'honeypot', // XARF spec - should be used - reporter: { - org: 'Security Team', - contact: 'security@example.com', - domain: 'example.com', - }, - sender: { - org: 'SOC', - contact: 'soc@example.com', - domain: 'example.com', - }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - first_seen: '2024-01-15T09:00:00Z', - source_port: 12345, - }, - }); - - // snake_case values should win - expect(report.type).toBe('port_scan'); - expect(report.source_identifier).toBe('192.0.2.50'); - expect(report.evidence_source).toBe('honeypot'); - }); - }); - - describe('error messages', () => { - it('should show both field name options in error messages', () => { - expect(() => { - generator.generateReport({ - category: 'connection', - type: 'ddos', - // Missing source_identifier/sourceIdentifier - reporter: { - org: 'Security Team', - contact: 'security@example.com', - domain: 'example.com', - }, - sender: { - org: 'SOC', - contact: 'soc@example.com', - domain: 'example.com', - }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - }, - }); - }).toThrow('source_identifier (or sourceIdentifier) is required'); - }); - - it('should show both field name options when type is missing', () => { - expect(() => { - generator.generateReport({ - category: 'connection', - // Missing type/reportType - source_identifier: '192.0.2.100', - reporter: { - org: 'Security Team', - contact: 'security@example.com', - domain: 'example.com', - }, - sender: { - org: 'SOC', - contact: 'soc@example.com', - domain: 'example.com', - }, - additionalFields: { - destination_ip: '203.0.113.50', - protocol: 'tcp', - }, - }); - }).toThrow('type (or reportType) is required'); - }); - }); - - describe('output always uses snake_case', () => { - it('should output snake_case even when input uses camelCase', () => { - const report = generator.generateReport({ - category: 'content', - reportType: 'phishing', // camelCase input - sourceIdentifier: '192.0.2.100', // camelCase input - evidenceSource: 'user_report', // camelCase input - reporter: { - org: 'Security Team', - contact: 'security@example.com', - domain: 'example.com', - }, - sender: { - org: 'SOC', - contact: 'soc@example.com', - domain: 'example.com', - }, - additionalFields: { - url: 'http://phishing.example.com', - }, - }); - - // Verify output uses snake_case field names - expect(report).toHaveProperty('type'); - expect(report).toHaveProperty('source_identifier'); - expect(report).toHaveProperty('evidence_source'); - - // Verify NO camelCase fields in output - const reportKeys = Object.keys(report); - expect(reportKeys).not.toContain('reportType'); - expect(reportKeys).not.toContain('sourceIdentifier'); - expect(reportKeys).not.toContain('evidenceSource'); - expect(reportKeys).not.toContain('onBehalfOf'); - }); - }); -}); diff --git a/tests/senior-engineer-feedback.test.ts b/tests/senior-engineer-feedback.test.ts index 7f22433..dd125d9 100644 --- a/tests/senior-engineer-feedback.test.ts +++ b/tests/senior-engineer-feedback.test.ts @@ -2,7 +2,7 @@ * Tests for senior engineer feedback issues * * Issues reported: - * 1. Snake case vs camel case - XARF spec uses snake_case, library should support both + * 1. Snake case - XARF spec uses snake_case throughout * 2. Invalid properties should emit warnings (silent failure is bad) * 3. ReportType should alias to type * 4. Timestamps should validate ISO format and throw if invalid @@ -221,8 +221,8 @@ describe('Senior Engineer Feedback Issues', () => { // Content report with url as direct field (not in additionalFields) const report = generator.generateReport({ category: 'content', - reportType: 'phishing', - sourceIdentifier: '192.0.2.100', + type: 'phishing', + source_identifier: '192.0.2.100', reporter: { org: 'Test Org', contact: 'test@example.com', @@ -246,8 +246,8 @@ describe('Senior Engineer Feedback Issues', () => { // Generate a content report with direct url field const report = generator.generateReport({ category: 'content', - reportType: 'phishing', - sourceIdentifier: '192.0.2.100', + type: 'phishing', + source_identifier: '192.0.2.100', reporter: { org: 'Test Org', contact: 'test@example.com', @@ -273,8 +273,8 @@ describe('Senior Engineer Feedback Issues', () => { // Connection report with fields as direct properties const report = generator.generateReport({ category: 'connection', - reportType: 'ddos', - sourceIdentifier: '192.0.2.100', + type: 'ddos', + source_identifier: '192.0.2.100', reporter: { org: 'Test Org', contact: 'test@example.com',