// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Vorion LLC
/**
 * Datadog SIEM Connector
 *
 * Datadog Logs API integration:
 * - Logs API v2 integration
 * - Tags and attributes mapping
 * - API key authentication
 * - Service and source configuration
 * - Gzip compression support
 *
 * @packageDocumentation
 * @module security/siem/datadog
 */
import { createLogger } from '../../common/logger.js';
import { BaseSIEMConnector, registerConnectorFactory } from './connector.js';
import { gzipSync } from 'zlib';
const logger = createLogger({ component: 'siem-datadog' });
// =============================================================================
// Datadog Site URLs
// =============================================================================
const DATADOG_SITES = {
    us1: 'https://http-intake.logs.datadoghq.com',
    us3: 'https://http-intake.logs.us3.datadoghq.com',
    us5: 'https://http-intake.logs.us5.datadoghq.com',
    eu1: 'https://http-intake.logs.datadoghq.eu',
    ap1: 'https://http-intake.logs.ap1.datadoghq.com',
};
// =============================================================================
// Datadog Connector
// =============================================================================
/**
 * Datadog connector for SIEM integration
 *
 * Features:
 * - Logs API v2 for efficient ingestion
 * - Tag-based event categorization
 * - Gzip compression for large payloads
 * - Multi-site support
 */
export class DatadogConnector extends BaseSIEMConnector {
    type = 'datadog';
    site;
    apiKey;
    service;
    source;
    hostname;
    defaultTags;
    compress;
    baseUrl;
    constructor(config) {
        super(config);
        this.site = config.site;
        this.apiKey = config.apiKey;
        this.service = config.service;
        this.source = config.source ?? 'vorion';
        this.hostname = config.hostname;
        this.defaultTags = config.tags ?? [];
        this.compress = config.compress ?? true;
        this.baseUrl = DATADOG_SITES[this.site];
        if (!this.baseUrl) {
            throw new Error(`Invalid Datadog site: ${this.site}`);
        }
        logger.info({
            name: this.name,
            site: this.site,
            service: this.service,
            source: this.source,
            compress: this.compress,
        }, 'DatadogConnector initialized');
    }
    /**
     * Connect to Datadog (verify API key)
     */
    async doConnect() {
        // Verify API key by checking validation endpoint
        const healthy = await this.doHealthCheck();
        if (!healthy) {
            throw new Error('Failed to validate Datadog API key');
        }
    }
    /**
     * Disconnect from Datadog (no-op for HTTP)
     */
    async doDisconnect() {
        // HTTP-based connection, no persistent connection to close
    }
    /**
     * Send events to Datadog Logs API
     */
    async doSend(events) {
        const batches = this.splitIntoBatches(events);
        let totalSent = 0;
        let totalFailed = 0;
        const failedIds = [];
        for (const batch of batches) {
            try {
                const result = await this.sendBatch(batch);
                totalSent += result.eventsSent;
                totalFailed += result.eventsFailed;
                if (result.failedEventIds) {
                    failedIds.push(...result.failedEventIds);
                }
            }
            catch (error) {
                totalFailed += batch.length;
                failedIds.push(...batch.map((e) => e.id));
                this.logger.error({ error, batchSize: batch.length }, 'Failed to send batch to Datadog');
            }
        }
        return {
            success: totalFailed === 0,
            eventsSent: totalSent,
            eventsFailed: totalFailed,
            durationMs: 0,
            failedEventIds: failedIds.length > 0 ? failedIds : undefined,
        };
    }
    /**
     * Send a single batch to Datadog
     */
    async sendBatch(events) {
        const logs = events.map((event) => this.toDatadogLog(event));
        const jsonBody = JSON.stringify(logs);
        const headers = {
            'DD-API-KEY': this.apiKey,
            'Content-Type': 'application/json',
        };
        let body = jsonBody;
        // Compress if enabled and payload is large enough
        if (this.compress && jsonBody.length > 1000) {
            body = gzipSync(jsonBody);
            headers['Content-Encoding'] = 'gzip';
        }
        const response = await this.httpRequest({
            url: `${this.baseUrl}/api/v2/logs`,
            method: 'POST',
            headers,
            body,
        });
        if (!response.ok) {
            throw new Error(`Datadog API error: ${response.status} - ${response.text}`);
        }
        return {
            success: true,
            eventsSent: events.length,
            eventsFailed: 0,
            durationMs: 0,
            response: response.data,
        };
    }
    /**
     * Health check - validate API key
     */
    async doHealthCheck() {
        try {
            // Use the validate endpoint
            const response = await this.httpRequest({
                url: 'https://api.datadoghq.com/api/v1/validate',
                method: 'GET',
                headers: {
                    'DD-API-KEY': this.apiKey,
                },
                timeout: 5000,
            });
            return response.ok;
        }
        catch (error) {
            this.logger.warn({ error: error.message }, 'Datadog health check failed');
            return false;
        }
    }
    /**
     * Convert security event to Datadog log format
     */
    toDatadogLog(event) {
        // Build tags array
        const tags = [
            ...this.defaultTags,
            `category:${event.category}`,
            `severity:${this.getSeverityName(event.severity)}`,
            `outcome:${event.outcome}`,
            `event_type:${event.eventType}`,
        ];
        if (event.environment) {
            tags.push(`env:${event.environment}`);
        }
        if (event.user?.tenantId) {
            tags.push(`tenant:${event.user.tenantId}`);
        }
        if (event.tags) {
            tags.push(...event.tags);
        }
        const log = {
            message: event.message,
            service: this.service,
            ddsource: this.source,
            ddtags: tags.join(','),
            '@timestamp': event.timestamp.toISOString(),
            // Event metadata
            event_id: event.id,
            event_type: event.eventType,
            event_category: event.category,
            event_severity: event.severity,
            event_severity_name: this.getSeverityName(event.severity),
            event_outcome: event.outcome,
            description: event.description,
            // Network attributes
            network: this.buildNetworkAttributes(event),
            // User attributes
            usr: this.buildUserAttributes(event),
            // Geo attributes
            geo: event.geo
                ? {
                    country: event.geo.country,
                    country_code: event.geo.countryCode,
                    city: event.geo.city,
                    region: event.geo.region,
                    latitude: event.geo.latitude,
                    longitude: event.geo.longitude,
                    timezone: event.geo.timezone,
                }
                : undefined,
            // Threat attributes
            threat: event.threat
                ? {
                    indicator: event.threat.indicator,
                    type: event.threat.threatType,
                    confidence: event.threat.confidence,
                    source: event.threat.source,
                    malware_families: event.threat.malwareFamilies,
                    threat_actors: event.threat.threatActors,
                    mitre_attack: event.threat.mitreAttack,
                }
                : undefined,
            // Process attributes
            process: event.processName || event.processId
                ? {
                    name: event.processName,
                    pid: event.processId,
                }
                : undefined,
            // File attributes
            file: event.filePath || event.fileHash
                ? {
                    path: event.filePath,
                    hash: event.fileHash,
                }
                : undefined,
            // Source attributes
            vorion: {
                source: event.source,
                component: event.component,
                version: event.version,
                request_id: event.requestId,
            },
            // Custom fields
            ...event.customFields,
        };
        if (this.hostname) {
            log.hostname = this.hostname;
        }
        // Remove undefined values
        return this.removeUndefined(log);
    }
    /**
     * Build network attributes
     */
    buildNetworkAttributes(event) {
        if (!event.sourceIp &&
            !event.destinationIp &&
            !event.protocol &&
            !event.httpUrl) {
            return undefined;
        }
        return {
            client: event.sourceIp
                ? {
                    ip: event.sourceIp,
                    port: event.sourcePort,
                }
                : undefined,
            destination: event.destinationIp
                ? {
                    ip: event.destinationIp,
                    port: event.destinationPort,
                }
                : undefined,
            protocol: event.protocol,
        };
    }
    /**
     * Build user attributes
     */
    buildUserAttributes(event) {
        if (!event.user) {
            return undefined;
        }
        return {
            id: event.user.userId,
            name: event.user.username,
            email: event.user.email,
            roles: event.user.roles,
            groups: event.user.groups,
            tenant_id: event.user.tenantId,
            tenant_name: event.user.tenantName,
            department: event.user.department,
            privileged: event.user.privileged,
        };
    }
    /**
     * Get severity name from numeric value
     */
    getSeverityName(severity) {
        switch (severity) {
            case 0:
                return 'unknown';
            case 1:
                return 'low';
            case 4:
                return 'medium';
            case 7:
                return 'high';
            case 10:
                return 'critical';
            default:
                return 'unknown';
        }
    }
    /**
     * Remove undefined values from object
     */
    removeUndefined(obj) {
        const result = {};
        for (const [key, value] of Object.entries(obj)) {
            if (value === undefined) {
                continue;
            }
            if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
                const cleaned = this.removeUndefined(value);
                if (Object.keys(cleaned).length > 0) {
                    result[key] = cleaned;
                }
            }
            else {
                result[key] = value;
            }
        }
        return result;
    }
}
// =============================================================================
// Factory Registration
// =============================================================================
/**
 * Create a Datadog connector
 */
export function createDatadogConnector(config) {
    return new DatadogConnector(config);
}
// Register the factory
registerConnectorFactory('datadog', (config) => createDatadogConnector(config));
//# sourceMappingURL=datadog.js.map