#!/bin/sh # P2P Intel Validator - Validate and score received IOCs . /lib/functions.sh INTEL_DIR="/var/lib/p2p-intel" RECEIVED_DIR="$INTEL_DIR/received" VALIDATED_DIR="$INTEL_DIR/validated" REJECTED_DIR="$INTEL_DIR/rejected" # Initialize validator validator_init() { mkdir -p "$RECEIVED_DIR" "$VALIDATED_DIR" "$REJECTED_DIR" } # Check if IOC is too old _is_too_old() { local timestamp="$1" local max_age_hours max_age_hours=$(uci -q get p2p-intel.validation.max_age_hours || echo "168") local max_age_seconds=$((max_age_hours * 3600)) local current_time current_time=$(date +%s) local age=$((current_time - timestamp)) [ "$age" -gt "$max_age_seconds" ] } # Check if peer has ZKP verification on blockchain _check_zkp_verified() { local peer_id="$1" local chain_file="/srv/secubox/mesh/chain.json" [ -f "$chain_file" ] || { echo "0"; return 1; } # Search for peer_zkp_verified block with matching peer_fp local found=0 jsonfilter -i "$chain_file" -e '@.blocks[*]' 2>/dev/null | while read -r block; do local btype=$(echo "$block" | jsonfilter -e '@.type' 2>/dev/null) [ "$btype" = "peer_zkp_verified" ] || continue local peer_fp=$(echo "$block" | jsonfilter -e '@.data.peer_fp' 2>/dev/null) local result=$(echo "$block" | jsonfilter -e '@.data.result' 2>/dev/null) # Match peer_id (could be ZKP fingerprint or node ID) if [ "$peer_fp" = "$peer_id" ] && [ "$result" = "ACCEPT" ]; then echo "1" return 0 fi done echo "0" } # Check if source is trusted (with ZKP bonus) _is_source_trusted() { local source_did="$1" local min_trust min_trust=$(uci -q get p2p-intel.validation.min_source_trust || echo "40") local zkp_bonus zkp_bonus=$(uci -q get p2p-intel.validation.zkp_trust_bonus || echo "20") local base_score=50 # Default for new/unknown peers # Get score from identity trust system if available if [ -f /usr/lib/secubox-identity/trust.sh ]; then . /usr/lib/secubox-identity/trust.sh base_score=$(trust_get_score "$source_did" 2>/dev/null || echo "50") fi # Add ZKP bonus if peer is ZKP-verified on blockchain local zkp_verified=$(_check_zkp_verified "$source_did") if [ "$zkp_verified" = "1" ]; then base_score=$((base_score + zkp_bonus)) logger -t p2p-intel "ZKP trust bonus applied for $source_did (score: $base_score)" fi [ "$base_score" -ge "$min_trust" ] } # Check if IP is in local subnet (whitelist) _is_local_ip() { local ip="$1" # Check common local ranges case "$ip" in 10.*|172.1[6-9].*|172.2[0-9].*|172.3[0-1].*|192.168.*|127.*) return 0 ;; *) return 1 ;; esac } # Validate single IOC validate_ioc() { local ioc="$1" local source_did="$2" local ioc_type value timestamp ioc_type=$(echo "$ioc" | jsonfilter -e '@.type' 2>/dev/null) value=$(echo "$ioc" | jsonfilter -e '@.value' 2>/dev/null) timestamp=$(echo "$ioc" | jsonfilter -e '@.collected_at' 2>/dev/null) # Check required fields if [ -z "$ioc_type" ] || [ -z "$value" ]; then echo '{"valid": false, "reason": "missing_fields"}' return 1 fi # Check age if [ -n "$timestamp" ] && _is_too_old "$timestamp"; then echo '{"valid": false, "reason": "too_old"}' return 1 fi # Check source trust if [ -n "$source_did" ]; then if ! _is_source_trusted "$source_did"; then echo '{"valid": false, "reason": "untrusted_source"}' return 1 fi fi # Type-specific validation case "$ioc_type" in ip) # Validate IP format if ! echo "$value" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then echo '{"valid": false, "reason": "invalid_ip_format"}' return 1 fi # Check whitelist local whitelist_local whitelist_local=$(uci -q get p2p-intel.application.whitelist_local || echo "1") if [ "$whitelist_local" = "1" ] && _is_local_ip "$value"; then echo '{"valid": false, "reason": "local_ip_whitelisted"}' return 1 fi ;; domain) # Basic domain format check if ! echo "$value" | grep -qE '^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$'; then echo '{"valid": false, "reason": "invalid_domain_format"}' return 1 fi ;; url) # Basic URL format check if ! echo "$value" | grep -qE '^https?://'; then echo '{"valid": false, "reason": "invalid_url_format"}' return 1 fi ;; hash) # Check hash format (MD5, SHA1, SHA256) local len=${#value} if [ "$len" != "32" ] && [ "$len" != "40" ] && [ "$len" != "64" ]; then echo '{"valid": false, "reason": "invalid_hash_format"}' return 1 fi ;; *) echo '{"valid": false, "reason": "unknown_ioc_type"}' return 1 ;; esac echo '{"valid": true}' return 0 } # Validate signed batch validate_batch() { local batch="$1" local batch_file="$2" local require_signature require_signature=$(uci -q get p2p-intel.validation.require_signature || echo "1") # Verify signature if required if [ "$require_signature" = "1" ]; then if [ -f /usr/lib/p2p-intel/signer.sh ]; then . /usr/lib/p2p-intel/signer.sh if ! verify_batch_signature "$batch"; then echo '{"valid": false, "reason": "invalid_signature"}' return 1 fi fi fi local signer signer=$(echo "$batch" | jsonfilter -e '@.signer' 2>/dev/null) # Extract and validate each IOC local valid_count=0 local invalid_count=0 local validated_iocs="[" local first=1 local iocs iocs=$(echo "$batch" | jsonfilter -e '@.iocs[*]' 2>/dev/null) echo "$iocs" | while read -r ioc; do [ -z "$ioc" ] && continue local result result=$(validate_ioc "$ioc" "$signer") if echo "$result" | grep -q '"valid": true'; then valid_count=$((valid_count + 1)) [ "$first" = "1" ] || validated_iocs="$validated_iocs," validated_iocs="$validated_iocs$ioc" first=0 else invalid_count=$((invalid_count + 1)) fi done validated_iocs="$validated_iocs]" # Save validated IOCs local output_file="$VALIDATED_DIR/$(date +%s)_$signer.json" echo "$validated_iocs" > "$output_file" cat </dev/null find "$REJECTED_DIR" -type f -mtime +7 -delete 2>/dev/null find "$RECEIVED_DIR" -type f -mtime +7 -delete 2>/dev/null } # Initialize on source validator_init