feat(security): isolate multi-tenant agent audit metrics, restrict CORS origins, exclude sqlite databases from git tracking, and add TDD test suite

This commit is contained in:
vanne committed 2026-07-01 23:26:48 +07:00
1 parent b816c1e570
commit e9f35c5a6b
20 files changed
+1819 -871

No files matched your search

+10 -4
View File
@@ -41,11 +41,17 @@ function explainAppOrPort(appLabel: string | null, domain: string | null, port:
"1813": "RADIUS Accounting Server"
};
const explanation = matches[portStr] || (label.toLowerCase().includes("tls") || label.toLowerCase().includes("https") ? "Encrypted Connection (SSL/TLS)" : "");
let mainLabel = label;
let subLabel = matches[portStr] || (label.toLowerCase().includes("tls") || label.toLowerCase().includes("https") ? "Encrypted Connection (SSL/TLS)" : "");
if (appLabel && appLabel.includes("(") && domain && domain.startsWith("Port ")) {
mainLabel = appLabel;
subLabel = domain;
}
return {
main: label || `Port ${port}`,
sub: explanation
main: mainLabel || `Port ${port}`,
sub: subLabel
};
}
+12 -1
View File
@@ -251,7 +251,18 @@ export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) {
{f.dst_ip && <IpDetails ip={f.dst_ip} />}
</div>
</td>
<td className="px-3 py-2 text-xs text-purple-300 max-w-[140px] truncate">{f.app_label || f.domain || "—"}</td>
<td className="px-3 py-2 whitespace-nowrap max-w-[200px]">
<div className="flex flex-col gap-0.5">
<span className="font-semibold text-purple-300 text-xs truncate" title={f.app_label || "—"}>
{f.app_label || "—"}
</span>
{f.domain && (
<span className="text-[10px] text-slate-500 italic truncate" title={f.domain}>
{f.domain}
</span>
)}
</div>
</td>
<td className="px-3 py-2 text-xs text-slate-400">{f.protocol || "—"}</td>
<td className="px-3 py-2 text-right text-xs text-cyan-400 whitespace-nowrap">{fmtBytes(f.download)}</td>
<td className="px-3 py-2 text-right text-xs text-green-400 whitespace-nowrap">{fmtBytes(f.upload)}</td>
+7 -7
View File
@@ -18,17 +18,17 @@ interface DataTableProps<T> {
getRowClassName?: (row: T) => string;
}
export function DataTable<T>({
data,
columns,
searchPlaceholder = "Search...",
export function DataTable<T>({
data,
columns,
searchPlaceholder = "Search...",
searchFilter,
isLoading,
getRowClassName
}: DataTableProps<T>) {
const [query, setQuery] = useState("");
const filteredData = searchFilter
const filteredData = searchFilter
? data.filter(row => searchFilter(row, query))
: data;
@@ -86,8 +86,8 @@ export function DataTable<T>({
</tr>
) : (
filteredData.map((row, i) => (
<tr
key={i}
<tr
key={i}
className={`hover:bg-primary/5 transition-colors duration-200 group ${getRowClassName ? getRowClassName(row) : ""}`}
>
{columns.map((col, j) => (
+10 -4
View File
@@ -55,11 +55,17 @@ function explainAppOrPort(appLabel: string | null, domain: string | null, port:
"1813": "RADIUS Accounting Server"
};
const explanation = matches[portStr] || (label.toLowerCase().includes("tls") || label.toLowerCase().includes("https") ? "Encrypted Connection (SSL/TLS)" : "");
let mainLabel = label;
let subLabel = matches[portStr] || (label.toLowerCase().includes("tls") || label.toLowerCase().includes("https") ? "Encrypted Connection (SSL/TLS)" : "");
if (appLabel && appLabel.includes("(") && domain && domain.startsWith("Port ")) {
mainLabel = appLabel;
subLabel = domain;
}
return {
main: label || `Port ${port}`,
sub: explanation
main: mainLabel || `Port ${port}`,
sub: subLabel
};
}
+79 -79
View File
@@ -661,117 +661,117 @@ export interface SecurityInfo {
}
export interface DeviceInfo {
device_label : string | null;
device_type : string | null;
os_label : string | null;
manufacturer : string | null;
mac_address : string | null;
is_new : number | null;
device_label: string | null;
device_type: string | null;
os_label: string | null;
manufacturer: string | null;
mac_address: string | null;
is_new: number | null;
}
export interface DeviceAppItem {
label : string; // domain name OR protocol name
sub_label : string | null; // protocol when label is domain; null otherwise
type : 'domain' | 'protocol' | 'port';
download : number;
upload : number;
flow_count : number;
label: string; // domain name OR protocol name
sub_label: string | null; // protocol when label is domain; null otherwise
type: 'domain' | 'protocol' | 'port';
download: number;
upload: number;
flow_count: number;
}
export interface DeviceDomainItem {
domain : string;
download : number;
upload : number;
flow_count : number;
domain: string;
download: number;
upload: number;
flow_count: number;
}
export interface DeviceEncryption {
encrypted_pct : number | null;
encrypted_bytes : number | null;
encrypted_pct: number | null;
encrypted_bytes: number | null;
unencrypted_bytes: number | null;
total_bytes : number | null;
risk_level : string | null;
total_bytes: number | null;
risk_level: string | null;
}
export interface DeviceServerItem {
server_type : string | null;
hostname : string | null;
port : number | null;
protocol : string | null;
os_label : string | null;
download : number;
upload : number;
detected_at : string | null;
server_type: string | null;
hostname: string | null;
port: number | null;
protocol: string | null;
os_label: string | null;
download: number;
upload: number;
detected_at: string | null;
}
export interface DevicePwdItem {
dst_ip : string | null;
dst_port : number | null;
protocol : string | null;
username : string | null;
severity : string | null;
download : number;
upload : number;
detected_at : string | null;
dst_ip: string | null;
dst_port: number | null;
protocol: string | null;
username: string | null;
severity: string | null;
download: number;
upload: number;
detected_at: string | null;
}
export interface DeviceReputationItem {
remote_ip : string | null;
local_ip : string | null;
reputation : string | null;
score : number | null;
country : string | null;
app_label : string | null;
blacklisted : boolean;
download : number;
upload : number;
remote_ip: string | null;
local_ip: string | null;
reputation: string | null;
score: number | null;
country: string | null;
app_label: string | null;
blacklisted: boolean;
download: number;
upload: number;
}
export interface DeviceVpnItem {
vpn_type : string | null;
remote_ip : string | null;
protocol : string | null;
country : string | null;
confidence : number | null;
download : number;
upload : number;
detected_at : string | null;
vpn_type: string | null;
remote_ip: string | null;
protocol: string | null;
country: string | null;
confidence: number | null;
download: number;
upload: number;
detected_at: string | null;
}
export interface DeviceEventItem {
event_type : string | null;
severity : string | null;
ip_address : string | null;
mac_address : string | null;
description : string | null;
event_at : string | null;
event_type: string | null;
severity: string | null;
ip_address: string | null;
mac_address: string | null;
description: string | null;
event_at: string | null;
}
export interface DeviceMacBandwidth {
mac_address : string;
manufacturer : string | null;
download : number;
upload : number;
total : number;
mac_address: string;
manufacturer: string | null;
download: number;
upload: number;
total: number;
}
export interface DeviceDetails {
ip : string;
mac_address : string | null;
total_download : number;
total_upload : number;
flow_count : number;
device_info : DeviceInfo;
top_apps : DeviceAppItem[];
top_domains : DeviceDomainItem[];
flows : DeviceFlowItem[];
encryption : DeviceEncryption | null;
server_discovery : DeviceServerItem[];
ip: string;
mac_address: string | null;
total_download: number;
total_upload: number;
flow_count: number;
device_info: DeviceInfo;
top_apps: DeviceAppItem[];
top_domains: DeviceDomainItem[];
flows: DeviceFlowItem[];
encryption: DeviceEncryption | null;
server_discovery: DeviceServerItem[];
unencrypted_passwords: DevicePwdItem[];
ip_reputation : DeviceReputationItem[];
vpn_detections : DeviceVpnItem[];
events : DeviceEventItem[];
mac_bandwidth : DeviceMacBandwidth | null;
ip_reputation: DeviceReputationItem[];
vpn_detections: DeviceVpnItem[];
events: DeviceEventItem[];
mac_bandwidth: DeviceMacBandwidth | null;
}