Files
jarvis/api/endpoints/netscan.php
T
Claude 3d16061709 Auth hardening: login rate-limiting + session fixation defenses
- login.php: Redis-backed per-IP rate limit (10 fails / 15 min lockout), keyed off CF-Connecting-IP/X-Forwarded-For so it sees the real client behind NPM; fails open if Redis is down

- login.php: session_regenerate_id(true) on successful auth (prevents session fixation)

- php.ini: session.use_strict_mode = 1 (reject unknown/attacker-supplied session IDs)

- netscan.php: constant-time hash_equals for the registration-key check (matches agent.php)

Cookie flags already HttpOnly + SameSite=Lax (verified live). Agent auth verified: missing/bad X-Agent-Key -> 401 on every machine action.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:31:57 -05:00

85 lines
3.1 KiB
PHP

<?php
// Network scan push endpoint — called by PVE1 cron with nmap results
// Authenticates via X-Registration-Key header (same key as agent installer)
define('NETSCAN_KEY', AGENT_REGISTRATION_KEY);
if ($method !== 'POST') {
echo json_encode(['error' => 'POST only']); exit;
}
$reqKey = $_SERVER['HTTP_X_REGISTRATION_KEY'] ?? '';
if (!hash_equals(NETSCAN_KEY, $reqKey)) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']); exit;
}
$body = file_get_contents('php://input');
$payload = json_decode($body, true);
$devices = $payload['devices'] ?? [];
if (empty($devices)) {
echo json_encode(['error' => 'No devices in payload']); exit;
}
$discoveredIPs = [];
$upserted = 0;
foreach ($devices as $d) {
$ip = trim($d['ip'] ?? '');
$mac = trim($d['mac'] ?? '');
$hostname = trim($d['hostname'] ?? '');
$vendor = trim($d['vendor'] ?? '');
// Respect explicit status from probe (e.g. phone probe knows if device is offline)
// Fall back to "online" for nmap results which only report reachable hosts
$status = in_array($d['status'] ?? '', ['online','offline']) ? $d['status'] : 'online';
if (!$ip) continue;
$discoveredIPs[] = $ip;
JarvisDB::execute(
'INSERT INTO network_devices (ip, mac, hostname, status, last_seen)
VALUES (?,?,?,?,NOW())
ON DUPLICATE KEY UPDATE
mac = COALESCE(NULLIF(VALUES(mac),""), mac),
hostname = COALESCE(NULLIF(VALUES(hostname),""), hostname),
status = VALUES(status),
last_seen = NOW()',
[$ip, $mac ?: null, $hostname ?: $vendor ?: null, $status]
);
if ($vendor) {
JarvisDB::execute(
'UPDATE network_devices SET device_type=? WHERE ip=? AND (device_type IS NULL OR device_type="")',
[$vendor, $ip]
);
}
// Store SIP registration status in kb_facts if provided (VoIP probe)
$sipStatus = trim($d['sip_status'] ?? '');
$extension = trim($d['extension'] ?? '');
if ($sipStatus && $extension && $extension !== 'none') {
JarvisDB::execute(
"INSERT INTO kb_facts (category, fact_key, fact_value, host)
VALUES ('voip', ?, ?, ?)
ON DUPLICATE KEY UPDATE fact_value=VALUES(fact_value), updated_at=NOW()",
["ext_{$extension}_sip", $sipStatus, $ip]
);
JarvisDB::execute(
"INSERT INTO kb_facts (category, fact_key, fact_value, host)
VALUES ('voip', ?, ?, ?)
ON DUPLICATE KEY UPDATE fact_value=VALUES(fact_value), updated_at=NOW()",
["ext_{$extension}_ip", $ip, $ip]
);
}
$upserted++;
}
// Mark anything NOT in this scan as offline if stale > 10 min
if (!empty($discoveredIPs)) {
$ph = implode(',', array_fill(0, count($discoveredIPs), '?'));
JarvisDB::execute(
"UPDATE network_devices SET status='offline'
WHERE ip NOT IN ($ph) AND last_seen < DATE_SUB(NOW(), INTERVAL 10 MINUTE)",
$discoveredIPs
);
}
echo json_encode(['ok' => true, 'upserted' => $upserted, 'total_discovered' => count($discoveredIPs)]);