Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions backend/app/api/v1/endpoints/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ async def generate_agent(

except Exception as e:
# Fallback to source code if compilation fails
print(f"Compilation failed: {e}, falling back to source")
logger.warning("Compilation failed: %s, falling back to source", e)
content = source_code
filename = f"nop_agent_{agent.name.replace(' ', '_')}.go"
is_binary = False
Expand Down Expand Up @@ -465,7 +465,7 @@ async def agent_websocket(
working_agent.name = f"{template_name}@{hostname}"
await db.commit()

print(f"Agent {working_agent.name} registered: {message}")
logger.info("Agent %s registered: %s", working_agent.name, message)

# Extract agent IP and auto-generate /24 network for discovery
# Prefer internal network IPs (10.x, 192.168.x) over Docker bridge IPs (172.x)
Expand Down Expand Up @@ -523,7 +523,7 @@ async def agent_websocket(
# Handle discovered assets
assets = message.get('assets', [])
count = await AgentDataService.ingest_asset_data(db, working_agent.id, assets)
print(f"Agent {working_agent.name} discovered {count} assets")
logger.debug("Agent %s discovered %d assets", working_agent.name, count)
await websocket.send_json({
"type": "asset_ack",
"count": count,
Expand All @@ -537,13 +537,13 @@ async def agent_websocket(
if 'flows' in message:
traffic['flows'] = message.get('flows', [])
success = await AgentDataService.ingest_traffic_data(db, working_agent.id, traffic)
print(f"Agent {working_agent.name} traffic data: {success}")
logger.debug("Agent %s traffic data: %s", working_agent.name, success)

elif msg_type == "host_data":
# Handle host information
host_info = message.get('host', {})
success = await AgentDataService.ingest_host_data(db, working_agent.id, host_info)
print(f"Agent {working_agent.name} host data: {success}")
logger.debug("Agent %s host data: %s", working_agent.name, success)

# Store host_info in agent_metadata for POV interface access
if host_info and working_agent:
Expand Down Expand Up @@ -592,12 +592,12 @@ async def agent_websocket(
}))

except json.JSONDecodeError:
print(f"Invalid JSON from agent {working_agent.name}")
logger.warning("Invalid JSON from agent %s", working_agent.name)

except WebSocketDisconnect:
print(f"Agent {working_agent.name if working_agent else agent.name} disconnected")
logger.info("Agent %s disconnected", working_agent.name if working_agent else agent.name)
except Exception as e:
print(f"WebSocket error for agent {working_agent.name if working_agent else agent.name}: {e}")
logger.error("WebSocket error for agent %s: %s", working_agent.name if working_agent else agent.name, e)
finally:
# Cleanup - use working_agent.id if available
cleanup_agent_id = str(working_agent.id) if working_agent else str(agent_id)
Expand Down
26 changes: 16 additions & 10 deletions backend/app/api/v1/endpoints/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Asset management endpoints
"""

import logging
from fastapi import APIRouter, Depends, HTTPException, status, Query, Request
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
Expand All @@ -10,10 +11,15 @@

from app.core.database import get_db
from app.core.pov_middleware import get_agent_pov
from app.schemas.asset import AssetCreate, AssetUpdate, AssetResponse, AssetList, AssetStats
from app.schemas.asset import (
AssetCreate, AssetUpdate, AssetResponse, AssetList, AssetStats,
OnlineAssetResponse, AssetClassificationResponse
)
from app.services.asset_service import AssetService
from app.models.asset import Asset

logger = logging.getLogger(__name__)

router = APIRouter()


Expand All @@ -34,7 +40,7 @@ async def get_assets(
- In POV view: agent_id filter overrides exclude_agent_assets
"""
agent_pov = get_agent_pov(request)
print(f"[ASSETS DEBUG] X-Agent-POV header: {request.headers.get('X-Agent-POV')}, agent_pov: {agent_pov}")
logger.debug("Agent POV header: %s, agent_pov: %s", request.headers.get('X-Agent-POV'), agent_pov)
asset_service = AssetService(db)
result = await asset_service.get_assets(
page=page,
Expand All @@ -45,7 +51,7 @@ async def get_assets(
agent_id=agent_pov,
exclude_agent_assets=exclude_agent_assets if not agent_pov else False
)
print(f"[ASSETS DEBUG] Returning {result.total} assets for agent_pov={agent_pov}")
logger.debug("Returning %d assets for agent_pov=%s", result.total, agent_pov)
return result


Expand All @@ -64,7 +70,7 @@ async def get_asset_stats(
)


@router.get("/online", response_model=List[dict])
@router.get("/online", response_model=List[OnlineAssetResponse])
async def get_online_assets(db: AsyncSession = Depends(get_db)):
"""Get list of all assets (online and offline) for dropdown"""
asset_service = AssetService(db)
Expand All @@ -75,16 +81,16 @@ async def get_online_assets(db: AsyncSession = Depends(get_db)):
)
# Return simplified list with IP, hostname, and status
return [
{
"ip_address": asset.ip_address,
"hostname": asset.hostname or asset.ip_address,
"status": asset.status
}
OnlineAssetResponse(
ip_address=str(asset.ip_address),
hostname=asset.hostname or str(asset.ip_address),
status=str(asset.status)
)
for asset in result.assets
]


@router.get("/classification")
@router.get("/classification", response_model=AssetClassificationResponse)
async def get_asset_classification(db: AsyncSession = Depends(get_db)):
"""Get asset classification breakdown by OS type"""
try:
Expand Down
13 changes: 7 additions & 6 deletions backend/app/api/v1/endpoints/traffic.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect, HTTPException, Request
from fastapi.responses import FileResponse
from typing import List, Dict, Optional
Expand All @@ -15,6 +16,8 @@
import subprocess
import time

logger = logging.getLogger(__name__)

router = APIRouter()

class PingRequest(BaseModel):
Expand Down Expand Up @@ -88,9 +91,9 @@ def packet_callback(packet_data):
await websocket.send_json(packet)

except WebSocketDisconnect:
print("Traffic WebSocket disconnected")
logger.debug("Traffic WebSocket disconnected")
except Exception as e:
print(f"WebSocket error: {e}")
logger.error("WebSocket error: %s", e)
finally:
# Only stop sniffing if NOT in persistent capture mode
if not sniffer_service.persistent_capture:
Expand Down Expand Up @@ -256,7 +259,7 @@ async def get_traffic_flows(

return {"flows": flows_list, "total": len(flows_list)}
except Exception as e:
print(f"Error getting flows: {e}")
logger.error("Error getting flows: %s", e)
return {"flows": [], "total": 0}

@router.get("/stats")
Expand Down Expand Up @@ -340,9 +343,7 @@ async def get_traffic_stats(
"agent_id": str(agent_pov)
}
except Exception as e:
print(f"Error getting agent traffic stats: {e}")
import traceback
traceback.print_exc()
logger.exception("Error getting agent traffic stats: %s", e)
return {
"total_packets": 0,
"total_bytes": 0,
Expand Down
20 changes: 20 additions & 0 deletions backend/app/schemas/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,23 @@ class AssetStats(BaseModel):
by_type: Dict[str, int]
by_vendor: Dict[str, int]
recently_discovered: int


class OnlineAssetResponse(BaseModel):
"""Simple asset response for dropdowns"""
ip_address: str
hostname: str
status: str


class AssetClassificationCategory(BaseModel):
"""Classification category"""
category: str
count: int
percentage: float


class AssetClassificationResponse(BaseModel):
"""Asset classification response"""
total: int
categories: List[AssetClassificationCategory]
2 changes: 1 addition & 1 deletion backend/app/services/SnifferService.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ def _run_sniff(self):
stop_filter=lambda p: not self.is_sniffing
)
except Exception as e:
print(f"Sniffing error: {e}")
logger.error("Sniffing error: %s", e)
self.is_sniffing = False

def start_sniffing(self, interface: str, callback: Optional[Callable], filter_str: Optional[str] = None, persistent: bool = False):
Expand Down
27 changes: 14 additions & 13 deletions backend/app/services/agent_data_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
with proper agent_id tagging.
"""

import logging
from typing import List, Dict, Any
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
Expand All @@ -16,6 +17,8 @@
from app.models.asset import Asset, AssetStatus
from app.models.flow import Flow

logger = logging.getLogger(__name__)


class AgentDataService:
"""Service for ingesting data from agents"""
Expand Down Expand Up @@ -78,7 +81,7 @@ async def ingest_asset_data(
processed += 1

except Exception as e:
print(f"Error processing asset {asset_data}: {e}")
logger.warning("Error processing asset %s: %s", asset_data, e)
continue

await db.commit()
Expand Down Expand Up @@ -119,24 +122,24 @@ async def ingest_traffic_data(
agent.agent_metadata = {}

# Store interfaces for POV mode
print(f"[TRAFFIC INGEST] Traffic data keys: {traffic.keys()}")
logger.debug("Traffic data keys: %s", traffic.keys())
if 'interfaces' in traffic:
print(f"[TRAFFIC INGEST] Storing {len(traffic['interfaces'])} interfaces")
logger.debug("Storing %d interfaces", len(traffic['interfaces']))
agent.agent_metadata['interfaces'] = traffic['interfaces']
agent.agent_metadata['last_traffic_update'] = datetime.utcnow().isoformat()
# Mark JSONB field as modified for SQLAlchemy
flag_modified(agent, 'agent_metadata')
else:
print(f"[TRAFFIC INGEST] No 'interfaces' key in traffic data")
logger.debug("No 'interfaces' key in traffic data")

# Store flows in database
print(f"[TRAFFIC INGEST] Checking for flows. 'flows' in traffic: {'flows' in traffic}")
logger.debug("Checking for flows. 'flows' in traffic: %s", 'flows' in traffic)
if 'flows' in traffic:
print(f"[TRAFFIC INGEST] flows value type: {type(traffic['flows'])}, length: {len(traffic['flows']) if traffic['flows'] else 0}")
logger.debug("flows value type: %s, length: %d", type(traffic['flows']), len(traffic['flows']) if traffic['flows'] else 0)

if 'flows' in traffic and traffic['flows']:
flows_data = traffic['flows']
print(f"[TRAFFIC INGEST] Processing {len(flows_data)} flows from agent {agent.name}")
logger.debug("Processing %d flows from agent %s", len(flows_data), agent.name)

for flow_data in flows_data:
try:
Expand All @@ -157,17 +160,15 @@ async def ingest_traffic_data(
)
db.add(flow)
except Exception as fe:
print(f"[TRAFFIC INGEST] Error creating flow: {fe}")
logger.warning("Error creating flow: %s", fe)

print(f"[TRAFFIC INGEST] Agent {agent.name} stored {len(flows_data)} flows")
logger.debug("Agent %s stored %d flows", agent.name, len(flows_data))

await db.commit()

return True
except Exception as e:
print(f"Error ingesting traffic data: {e}")
import traceback
traceback.print_exc()
logger.exception("Error ingesting traffic data: %s", e)
return False

@staticmethod
Expand Down Expand Up @@ -212,7 +213,7 @@ async def ingest_host_data(

return False
except Exception as e:
print(f"Error ingesting host data: {e}")
logger.exception("Error ingesting host data: %s", e)
return False

@staticmethod
Expand Down
2 changes: 1 addition & 1 deletion backend/app/services/agent_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ async def update_agent(db: AsyncSession, agent_id: UUID, agent_data: AgentUpdate
"settings": agent.settings or {}
})
except Exception as e:
print(f"Failed to send settings update to agent {agent_id}: {e}")
logger.warning("Failed to send settings update to agent %s: %s", agent_id, e)

return agent

Expand Down
Loading