Best Practices
This document outlines best practices for developing, deploying, and maintaining secure and effective reactions in Jibril's runtime security system.
Security Best Practices
Input Validation and Sanitization
Always validate and sanitize input data to prevent injection attacks and ensure reliability.
function process(data) {
// Validate required fields
if (!data || typeof data !== 'object') {
Error("Invalid data object received");
return;
}
// Validate process information
if (data.process) {
// Sanitize command strings to prevent log injection
let cmd = data.process.cmd || "unknown";
cmd = cmd.replace(/[\x00-\x1f\x7f-\x9f]/g, ''); // Remove control characters
cmd = cmd.length > 500 ? cmd.substring(0, 500) + "..." : cmd;
Info("Sanitized command: " + cmd);
}
// Validate file paths
if (data.file && data.file.file) {
let filePath = data.file.file;
// Ensure path is absolute and doesn't contain traversal attempts
if (!filePath.startsWith('/') || filePath.includes('../')) {
Error("Invalid file path detected: " + filePath);
return;
}
}
}
Principle of Least Privilege
Only use the minimum privileges and capabilities required for your reaction.
function process(data) {
// Good: Check conditions before taking drastic actions
if (data.metadata && data.metadata.importance === "critical") {
// Only escalate for truly critical events
let result = KillCurrent();
if (result === 0) {
Info("Critical threat terminated");
}
} else {
// For non-critical events, use lighter responses
Warn("Suspicious activity logged: " + data.process.cmd);
// Log for later analysis instead of immediate action
DataSet("suspicious_" + new Date().getTime(), JSON.stringify({
process: data.process.cmd,
timestamp: new Date().toISOString()
}));
}
}
Safe File Operations
Implement secure file handling practices to prevent unauthorized access.
function process(data) {
// Create evidence collection with proper security
let forensicDir = CreateTempDir("incident-*");
if (forensicDir === "") {
Error("Failed to create secure temporary directory");
return;
}
// Validate that the directory is in expected location
if (!forensicDir.startsWith("/tmp/")) {
Error("Temporary directory created in unexpected location");
return;
}
// Create evidence file with safe content
let evidence = {
timestamp: new Date().toISOString(),
event_id: uuid,
// Only include safe, non-sensitive data
process_name: data.process ? data.process.exe : "unknown",
file_accessed: data.file ? data.file.basename : "unknown"
// Avoid including full command lines or sensitive data
};
let evidencePath = forensicDir + "/evidence.json";
let writeResult = WriteFile(evidencePath, JSON.stringify(evidence, null, 2));
if (writeResult === 0) {
Info("Evidence collected securely at: " + evidencePath);
} else {
Error("Failed to write evidence: " + Errno());
}
}
Network Security Considerations
Be cautious with network blocking operations to avoid disrupting legitimate services.
function process(data) {
// Extract network information safely
let remoteIps = [];
let remoteDomains = [];
if (data.background && data.background.flows && data.background.flows.protocols) {
for (let protocol of data.background.flows.protocols) {
if (protocol.pairs) {
for (let pair of protocol.pairs) {
if (pair.nodes && pair.nodes.remote) {
// Validate IP addresses before adding
let ip = pair.nodes.remote.address;
if (ip && /^(\d{1,3}\.){3}\d{1,3}$/.test(ip)) {
remoteIps.push(ip);
}
// Validate domain names
if (pair.nodes.remote.names) {
for (let domain of pair.nodes.remote.names) {
if (domain && domain.length > 0 && domain.length < 255) {
remoteDomains.push(domain);
}
}
}
}
}
}
}
}
// Apply whitelist logic to prevent blocking critical infrastructure
let safeToBlock = [];
let whitelist = [
"127.0.0.1", "localhost",
/^10\./, /^192\.168\./, /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
"company-domain.com", "trusted-partner.org"
];
for (let ip of remoteIps) {
let shouldBlock = true;
for (let pattern of whitelist) {
if (pattern instanceof RegExp) {
if (pattern.test(ip)) {
shouldBlock = false;
break;
}
} else if (ip === pattern) {
shouldBlock = false;
break;
}
}
if (shouldBlock) {
safeToBlock.push(ip);
}
}
// Only block if we have valid targets
if (safeToBlock.length > 0) {
Info("Blocking " + safeToBlock.length + " suspicious IPs");
let result = NetBlockIp();
if (result === 0) {
Info("Network blocking successful");
}
}
}
Performance Best Practices
Efficient Data Processing
Optimize data processing to minimize reaction execution time.
function process(data) {
// Cache frequently accessed data
let processCmd = data.process ? data.process.cmd : "";
let fileName = data.file ? data.file.basename : "";
// Use early returns to avoid unnecessary processing
if (!processCmd && !fileName) {
Info("No actionable data in event");
return;
}
// Efficient pattern matching
let suspiciousPatterns = ["wget", "curl", "nc", "bash -i"];
let isSuspicious = suspiciousPatterns.some(pattern => processCmd.includes(pattern));
if (isSuspicious) {
// Only perform expensive operations when necessary
Info("Suspicious command detected: " + processCmd);
// Batch data store operations
let timestamp = new Date().toISOString();
DataSet("last_suspicious", timestamp);
let count = parseInt(DataGet("suspicious_count") || "0") + 1;
DataSet("suspicious_count", String(count));
// Conditional expensive operations
if (count > 10) {
Warn("High frequency suspicious activity - escalating");
NetBlockIp();
}
}
}
Memory Management
Use memory efficiently to prevent resource exhaustion.
function process(data) {
// Avoid storing large objects in the data store
let summary = {
timestamp: new Date().toISOString(),
process: data.process ? data.process.exe : "unknown",
// Don't store the entire data object
event_type: kind
};
// Use string keys that are likely to be reused
let dateKey = new Date().toDateString();
let countKey = "events_" + dateKey;
// Implement rotation to prevent unlimited growth
let eventCount = parseInt(DataGet(countKey) || "0") + 1;
DataSet(countKey, String(eventCount));
// Clean up old data periodically
if (eventCount % 100 === 0) {
// Remove data older than 7 days
let cutoff = new Date();
cutoff.setDate(cutoff.getDate() - 7);
let keys = JSON.parse(DataKeys());
for (let key of keys) {
if (key.startsWith("events_")) {
let keyDate = new Date(key.replace("events_", ""));
if (keyDate < cutoff) {
DataDelete(key);
}
}
}
}
}
Concurrent Execution Optimization
Design reactions to work efficiently when running in parallel.
function process(data) {
// Use unique identifiers to avoid conflicts between parallel reactions
let reactionId = name + "_" + uuid.slice(-8);
let lockKey = "processing_" + reactionId;
// Simple distributed locking mechanism
if (DataHasKey(lockKey)) {
Info("Another instance is processing this event");
return;
}
// Set processing lock
DataSet(lockKey, new Date().toISOString());
try {
// Perform reaction logic
Info("Processing event: " + uuid);
// Use atomic operations where possible
let globalCount = parseInt(DataGet("global_event_count") || "0") + 1;
DataSet("global_event_count", String(globalCount));
} finally {
// Always clean up the lock
DataDelete(lockKey);
}
}
Testing and Quality Assurance
Comprehensive Testing Strategy
Implement thorough testing before deploying reactions to production.
// Test Reaction Template
function process(data) {
let testMode = DataGet("test_mode") === "true";
if (testMode) {
Info("=== RUNNING IN TEST MODE ===");
// Test all helper functions
testHelperFunctions();
// Test data processing
testDataProcessing(data);
// Test error handling
testErrorHandling();
Info("=== TEST MODE COMPLETE ===");
return;
}
// Normal reaction logic
processEvent(data);
}
function testHelperFunctions() {
// Test logging functions
Info("Testing Info() function");
Warn("Testing Warn() function");
Error("Testing Error() function");
// Test data store functions
DataSet("test_key", "test_value");
let retrieved = DataGet("test_key");
Info("Data store test: " + (retrieved === "test_value" ? "PASS" : "FAIL"));
DataDelete("test_key");
// Test file operations (safe)
let testContent = "Test at " + new Date().toISOString();
let tmpDir = CreateTempDir("test-*");
if (tmpDir !== "") {
let testFile = tmpDir + "/test.txt";
let writeResult = WriteFile(testFile, testContent);
let readContent = ReadFile(testFile);
Info("File I/O test: " + (readContent === testContent ? "PASS" : "FAIL"));
}
// Test error function
let errno = Errno();
Info("Errno test: " + (typeof errno === "string" ? "PASS" : "FAIL"));
}
function testDataProcessing(data) {
Info("Testing data structure validation");
// Test data presence
Info("Data object type: " + typeof data);
Info("Data keys: " + Object.keys(data).join(", "));
// Test process data
if (data.process) {
Info("Process data available");
Info("Process cmd: " + (data.process.cmd || "N/A"));
Info("Process pid: " + (data.process.pid || "N/A"));
}
// Test file data
if (data.file) {
Info("File data available");
Info("File path: " + (data.file.file || "N/A"));
Info("File actions: " + (data.file.actions ? data.file.actions.join(", ") : "N/A"));
}
}
function testErrorHandling() {
Info("Testing error handling");
// Test invalid file operations
let invalidRead = ReadFile("/nonexistent/path");
Info("Invalid read test: " + (invalidRead === "" ? "PASS" : "FAIL"));
// Test invalid data store operations
let invalidGet = DataGet("nonexistent_key");
Info("Invalid get test: " + (invalidGet === "" ? "PASS" : "FAIL"));
}
Staging Environment Testing
Create a comprehensive staging environment for reaction testing.
# Staging Test Configuration
- kind: staging_test
name: comprehensive_staging_test
enabled: false # Enable only in staging
version: 1.0
description: Comprehensive staging test for reactions
breed: file_access
mechanism: file_access
importance: low
bases:
- dir: /tmp/staging_test
regex: ".*"
file_actions:
- write
- unlink
reactions:
- format: js
code: |
function process(data) {
Info("=== STAGING TEST EXECUTION ===");
// Enable test mode
DataSet("test_mode", "true");
// Test basic functionality
testReactionCapabilities(data);
// Test performance under load
testPerformance();
// Test error scenarios
testErrorScenarios();
// Clean up test data
DataDelete("test_mode");
Info("=== STAGING TEST COMPLETE ===");
}
function testReactionCapabilities(data) {
Info("Testing reaction capabilities");
// Test all major helper functions
let capabilities = [
"logging", "data_store", "file_ops", "network_ops"
];
for (let capability of capabilities) {
Info("Testing capability: " + capability);
switch (capability) {
case "logging":
Info("Info logging works");
Warn("Warning logging works");
Error("Error logging works");
break;
case "data_store":
DataSet("cap_test", "success");
let value = DataGet("cap_test");
Info("Data store: " + (value === "success" ? "PASS" : "FAIL"));
DataDelete("cap_test");
break;
case "file_ops":
let tmpDir = CreateTempDir("cap-test-*");
if (tmpDir !== "") {
let testPath = tmpDir + "/test.txt";
WriteFile(testPath, "test content");
let content = ReadFile(testPath);
Info("File ops: " + (content === "test content" ? "PASS" : "FAIL"));
}
break;
case "network_ops":
// Only test if netpolicy is available
Info("Network ops capability available");
break;
}
}
}
function testPerformance() {
Info("Testing performance characteristics");
let startTime = new Date().getTime();
// Simulate typical reaction workload
for (let i = 0; i < 100; i++) {
DataSet("perf_test_" + i, "value_" + i);
}
let midTime = new Date().getTime();
for (let i = 0; i < 100; i++) {
DataGet("perf_test_" + i);
}
let endTime = new Date().getTime();
// Clean up
for (let i = 0; i < 100; i++) {
DataDelete("perf_test_" + i);
}
let writeTime = midTime - startTime;
let readTime = endTime - midTime;
Info("Performance test - Write: " + writeTime + "ms, Read: " + readTime + "ms");
}
Operational Best Practices
Monitoring and Alerting
Implement comprehensive monitoring for reaction health and effectiveness.
function process(data) {
let startTime = new Date().getTime();
try {
// Main reaction logic
performReactionLogic(data);
// Track success metrics
let successCount = parseInt(DataGet("reaction_success_count") || "0") + 1;
DataSet("reaction_success_count", String(successCount));
} catch (error) {
// Track failure metrics
let failureCount = parseInt(DataGet("reaction_failure_count") || "0") + 1;
DataSet("reaction_failure_count", String(failureCount));
Error("Reaction failed: " + error.toString());
// Alert on high failure rates
let totalAttempts = successCount + failureCount;
if (totalAttempts > 100 && (failureCount / totalAttempts) > 0.1) {
Error("ALERT: Reaction failure rate exceeds 10%");
}
} finally {
// Track performance metrics
let executionTime = new Date().getTime() - startTime;
let avgTimeKey = "avg_execution_time";
let currentAvg = parseFloat(DataGet(avgTimeKey) || "0");
let newAvg = (currentAvg * 0.9) + (executionTime * 0.1); // Rolling average
DataSet(avgTimeKey, String(newAvg));
if (executionTime > 5000) { // Alert on slow reactions
Warn("Slow reaction execution: " + executionTime + "ms");
}
}
}
Configuration Management
Maintain reaction configurations with proper version control and rollback capabilities.
# Production Deployment Template
- kind: production_reaction
name: managed_security_response
enabled: true
version: 2.1.0 # Always version your reactions
description: |
Production security response reaction
Change Log:
v2.1.0 - Added enhanced error handling and performance monitoring
v2.0.0 - Implemented graduated response system
v1.0.0 - Initial implementation
Deployment Notes:
- Requires netpolicy plugin enabled
- Tested in staging environment
- Approved by security team
breed: execution
mechanism: execution
tactic: execution
technique: T1059
importance: high
# Configuration parameters (document all settings)
arbitrary:
- how: OR
which: pertinent
items:
- what: cmd
which: contains
pattern: "malicious_pattern"
reactions:
- format: js
code: |
// Production Reaction - Version 2.1.0
function process(data) {
// Configuration constants
const VERSION = "2.1.0";
const MAX_EXECUTION_TIME = 5000; // 5 seconds
const FAILURE_THRESHOLD = 0.1; // 10%
Info("Production reaction v" + VERSION + " executing");
// Implement production logic with full error handling
try {
executeProductionLogic(data);
} catch (error) {
handleProductionError(error, data);
}
}
function executeProductionLogic(data) {
// Validated production logic
Info("Executing validated production response");
// Implementation details...
}
function handleProductionError(error, data) {
Error("Production reaction error: " + error.toString());
// Log error details for debugging
let errorRecord = {
timestamp: new Date().toISOString(),
version: "2.1.0",
error: error.toString(),
event_uuid: uuid,
data_summary: {
has_process: !!data.process,
has_file: !!data.file,
event_type: kind
}
};
WriteFile("/var/log/jibril/reaction-errors.log",
JSON.stringify(errorRecord) + "\n");
}
Documentation
Maintain comprehensive documentation for all reactions.
/**
* Advanced Incident Response Reaction
*
* Purpose: Provides comprehensive incident response capabilities including
* containment, evidence collection, and alerting.
*
* Triggers: Executes on critical security events detected by Jibril
*
* Dependencies:
* - netpolicy plugin (for network blocking)
* - Write access to /var/log/security/
* - Temporary directory creation permissions
*
* Configuration:
* - importance: critical (required for execution)
* - breed: execution, file_access, or network_peers
*
* Outputs:
* - Forensic evidence in temporary directories
* - Incident logs in /var/log/security/incidents.log
* - Real-time alerts via error logging
*
* Error Handling:
* - Graceful degradation on helper function failures
* - Comprehensive error logging
* - Rollback capabilities for network blocks
*
* Performance:
* - Target execution time: < 2 seconds
* - Memory usage: < 50MB temporary storage
* - Network calls: Minimal, only for blocking
*
* Testing:
* - Unit tests in staging environment
* - Integration tests with full event pipeline
* - Performance benchmarks under load
*
* Maintenance:
* - Review quarterly for effectiveness
* - Update threat intelligence patterns monthly
* - Monitor performance metrics continuously
*
* Version History:
* v1.0 - Initial implementation
* v1.1 - Added performance monitoring
* v2.0 - Enhanced evidence collection
*
* Author: Security Team
* Last Updated: 2025-07-23
* Next Review: 2025-10-23
*/
function process(data) {
// Implementation follows documented specifications
implementIncidentResponse(data);
}
Compliance and Auditing
Audit Trail Implementation
Maintain comprehensive audit trails for all reaction activities.
function process(data) {
// Create audit record for all reaction executions
let auditRecord = {
timestamp: new Date().toISOString(),
reaction_name: name,
reaction_kind: kind,
event_uuid: uuid,
event_metadata: {
importance: data.metadata ? data.metadata.importance : "unknown",
tactic: data.metadata ? data.metadata.tactic : "unknown",
technique: data.metadata ? data.metadata.technique : "unknown"
},
actions_taken: [],
data_accessed: [],
files_created: [],
network_operations: []
};
try {
// Log data access
if (data.process) {
auditRecord.data_accessed.push("process_data");
}
if (data.file) {
auditRecord.data_accessed.push("file_data");
}
if (data.background && data.background.flows) {
auditRecord.data_accessed.push("network_flow_data");
}
// Execute reaction with audit logging
if (shouldTerminateProcess(data)) {
let killResult = KillCurrent();
auditRecord.actions_taken.push({
action: "process_termination",
result: killResult === 0 ? "success" : "failed",
error: killResult !== 0 ? Errno() : null
});
}
if (shouldBlockNetwork(data)) {
let blockResult = NetBlockIp();
auditRecord.actions_taken.push({
action: "network_blocking",
result: blockResult === 0 ? "success" : "failed",
error: blockResult !== 0 ? Errno() : null
});
auditRecord.network_operations.push("ip_blocking");
}
// Document evidence collection
let evidenceDir = CreateTempDir("audit-evidence-*");
if (evidenceDir !== "") {
let evidencePath = evidenceDir + "/reaction_evidence.json";
WriteFile(evidencePath, JSON.stringify(data, null, 2));
auditRecord.files_created.push({
path: evidencePath,
purpose: "evidence_collection",
size: JSON.stringify(data).length
});
}
auditRecord.status = "completed";
} catch (error) {
auditRecord.status = "failed";
auditRecord.error = error.toString();
Error("Reaction execution failed: " + error.toString());
}
// Write audit record
let auditPath = "/var/log/jibril/reaction-audit.log";
WriteFile(auditPath, JSON.stringify(auditRecord) + "\n");
// Store audit record in data store for reporting
DataSet("audit_" + uuid, JSON.stringify(auditRecord));
}
function shouldTerminateProcess(data) {
// Implement business logic for process termination decisions
return data.metadata && data.metadata.importance === "critical";
}
function shouldBlockNetwork(data) {
// Implement business logic for network blocking decisions
return data.background && data.background.flows &&
data.metadata && data.metadata.importance === "high";
}
Last updated