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:
1 parent
b816c1e570
commit
e9f35c5a6b
20 files changed
+1819
-871
No files matched your search
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user