3 Commits
16 changed files with 46 additions and 251 deletions
+1 -1
View File
@@ -625,7 +625,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Argus, an all in one network utility
Copyright (C) 2026 James Brotosky
Copyright (C) 2026 Jamse Brotosky
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
-6
View File
@@ -1,6 +0,0 @@
#!/bin/bash
cd src-tauri && cargo clean
cargo clippy --fix --all-targets --allow-dirty
cargo fmt
cargo tauri build
cargo tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc
+4 -3
View File
@@ -1,11 +1,12 @@
use pulldown_cmark::{html, Options, Parser};
use pulldown_cmark::{Options, Parser, html};
const CHANGELOG_MD: &str = include_str!("../../../CHANGELOG.md");
#[tauri::command]
pub async fn get_changelog() -> Result<String, String> {
let options =
Options::ENABLE_HEADING_ATTRIBUTES | Options::ENABLE_STRIKETHROUGH | Options::ENABLE_TABLES;
let options = Options::ENABLE_HEADING_ATTRIBUTES
| Options::ENABLE_STRIKETHROUGH
| Options::ENABLE_TABLES;
let parser = Parser::new_ext(CHANGELOG_MD, options);
let mut html_output = String::new();
+2 -1
View File
@@ -11,7 +11,8 @@ pub struct ExportRequest {
#[tauri::command]
pub async fn export_csv(request: ExportRequest) -> Result<(), String> {
let file = File::create(&request.path).map_err(|e| format!("Failed to create file: {}", e))?;
let file = File::create(&request.path)
.map_err(|e| format!("Failed to create file: {}", e))?;
let mut writer = csv::Writer::from_writer(file);
+1 -10
View File
@@ -40,16 +40,7 @@ pub async fn start_scan(app: AppHandle, config: ScanConfig) -> Result<(), String
let port_timeout_ms = config.port_timeout_ms.unwrap_or(1000);
tokio::spawn(async move {
ping_sweep(
&app,
&ips,
timeout_ms,
cancel,
port_scan,
ports,
port_timeout_ms,
)
.await;
ping_sweep(&app, &ips, timeout_ms, cancel, port_scan, ports, port_timeout_ms).await;
});
Ok(())
}
+2 -2
View File
@@ -6,7 +6,7 @@ pub enum HostStatus {
Up,
Down,
Filtered,
Unknown,
Unknown
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -42,7 +42,7 @@ pub struct PortResult {
pub struct NetbiosInfo {
pub computer_name: Option<String>,
pub workgroup: Option<String>,
pub mac_from_netbios: Option<String>,
pub mac_from_netbios: Option<String>
}
#[derive(Debug, Clone, Serialize, Deserialize)]
+3 -1
View File
@@ -3,7 +3,9 @@ use std::net::IpAddr;
/// Perform reverse DNS lookup on a single IP.
/// Returns the hostname if found, None otherwise.
pub async fn reverse_lookup(ip: IpAddr) -> Option<String> {
let result = tokio::task::spawn_blocking(move || dns_lookup::lookup_addr(&ip)).await;
let result = tokio::task::spawn_blocking(move || {
dns_lookup::lookup_addr(&ip)
}).await;
match result {
Ok(Ok(hostname)) => {
+1 -1
View File
@@ -1,2 +1,2 @@
pub mod dns;
pub mod range;
pub mod dns;
+1 -1
View File
@@ -1,3 +1,3 @@
pub mod netbios;
pub mod ping;
pub mod ports;
pub mod netbios;
+2 -1
View File
@@ -91,7 +91,8 @@ pub async fn ping_sweep(
let nb_fut = query_netbios(enriched.ip, 500);
if port_scan {
let scan_ports = ports2.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
let scan_ports = ports2
.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
let (nb_result, port_results) = tokio::join!(
nb_fut,
scan_host_ports(enriched.ip, &scan_ports, port_timeout_ms)
+10 -15
View File
@@ -42,7 +42,8 @@ fn service_name(port: u16) -> Option<String> {
}
pub const DEFAULT_PORTS: &[u16] = &[
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995, 1433, 1521, 3306, 3389,
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143,
443, 445, 993, 995, 1433, 1521, 3306, 3389,
5432, 5900, 5985, 5986, 8080, 8088, 8443,
];
@@ -95,10 +96,7 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
};
if let Some(data) = probe {
if timeout(banner_timeout, stream.write_all(data))
.await
.is_err()
{
if timeout(banner_timeout, stream.write_all(data)).await.is_err() {
return None;
}
}
@@ -109,15 +107,8 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
Ok(Ok(n)) if n > 0 => {
buf.truncate(n);
// Strip to printable ASCII, replace control chars
let banner: String = buf
.iter()
.map(|&b| {
if (0x20..0x7f).contains(&b) {
b as char
} else {
' '
}
})
let banner: String = buf.iter()
.map(|&b| if b >= 0x20 && b < 0x7f { b as char } else { ' ' })
.collect();
let banner = banner.trim().to_string();
if banner.is_empty() {
@@ -133,7 +124,11 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
const MAX_PORT_CONCURRENT: usize = 64;
pub async fn scan_host_ports(ip: IpAddr, ports: &[u16], timeout_ms: u64) -> Vec<PortResult> {
pub async fn scan_host_ports(
ip: IpAddr,
ports: &[u16],
timeout_ms: u64,
) -> Vec<PortResult> {
let mut results = Vec::with_capacity(ports.len());
for chunk in ports.chunks(MAX_PORT_CONCURRENT) {
-5
View File
@@ -211,11 +211,6 @@
<option value="nord">Nord</option>
<option value="solarized">Solarized Dark</option>
<option value="mocha">Catppuccin Mocha</option>
<option value="onedark">One Dark</option>
<option value="rosepine">Rosé Pine</option>
<option value="rosepine-moon">Rosé Pine Moon</option>
<option value="tokyonight">Tokyo Night</option>
<option value="iceberg">Iceberg</option>
</select>
<span class="status-bar-item" id="privilege-status">unprivileged</span>
</div>
+10 -195
View File
@@ -1456,7 +1456,7 @@ body {
--border: #0d4450;
--border-bright: #1a5c6a;
--text-primary: #cb4b16;
--text-primary: #93a1a1;
--text-secondary: #839496;
--text-muted: #586e75;
@@ -1489,11 +1489,11 @@ body {
--border: #313244;
--border-bright: #45475a;
--text-primary: #94e2d5;
--text-primary: #cdd6f4;
--text-secondary: #a6adc8;
--text-muted: #6c7086;
--cyan: #b4befe;
--cyan: #89b4fa;
--cyan-hover: #7ba4eb;
--cyan-dim: rgba(137, 180, 250, 0.08);
--cyan-glow: rgba(137, 180, 250, 0.2);
@@ -1515,209 +1515,24 @@ body {
filter: hue-rotate(40deg) saturate(0.6) brightness(0.9);
}
:root[data-theme="mocha"] .port-tag {
:root[data-theme="latte"] .port-tag {
background: rgba(30, 102, 245, 0.1);
border-color: rgba(30, 102, 245, 0.2);
}
:root[data-theme="mocha"] #privilege-status::before {
:root[data-theme="latte"] #privilege-status::before {
background: var(--status-up);
box-shadow: 0 0 4px rgba(64, 160, 43, 0.3);
}
:root[data-theme="mocha"] ::-webkit-scrollbar-track {
:root[data-theme="latte"] ::-webkit-scrollbar-track {
background: var(--bg-primary);
}
:root[data-theme="mocha"] ::-webkit-scrollbar-thumb {
:root[data-theme="latte"] ::-webkit-scrollbar-thumb {
background: var(--border-bright);
}
/* ===== Theme: One Dark ===== */
:root[data-theme="onedark"] {
--bg-primary: #282c34;
--bg-secondary: #21252b;
--bg-tertiary: #2c313a;
--bg-hover: #3e4451;
--bg-active: #4b5263;
--border: #3e4451;
--border-bright: #4b5263;
--text-primary: #abb2bf;
--text-secondary: #848b98;
--text-muted: #5c6370;
--cyan: #61afef;
--cyan-hover: #519fdf;
--cyan-dim: rgba(97, 175, 239, 0.08);
--cyan-glow: rgba(97, 175, 239, 0.2);
--cyan-subtle: rgba(97, 175, 239, 0.03);
--status-up: #98c379;
--status-up-glow: rgba(152, 195, 121, 0.15);
--status-down: #e06c75;
--status-down-glow: rgba(224, 108, 117, 0.15);
--status-filtered: #e5c07b;
--status-filtered-glow: rgba(229, 192, 123, 0.15);
--status-unknown: #5c6370;
--danger: #e06c75;
--danger-hover: #d05f68;
}
:root[data-theme="onedark"] .toolbar-circuit {
filter: hue-rotate(30deg) saturate(0.6);
}
/* ===== Theme: Rosé Pine ===== */
:root[data-theme="rosepine"] {
--bg-primary: #191724;
--bg-secondary: #1f1d2e;
--bg-tertiary: #26233a;
--bg-hover: #403d52;
--bg-active: #524f67;
--border: #26233a;
--border-bright: #403d52;
--text-primary: #31748f;
--text-secondary: #908caa;
--text-muted: #6e6a86;
--cyan: #c4a7e7;
--cyan-hover: #b498d7;
--cyan-dim: rgba(196, 167, 231, 0.08);
--cyan-glow: rgba(196, 167, 231, 0.2);
--cyan-subtle: rgba(196, 167, 231, 0.03);
--status-up: #9ccfd8;
--status-up-glow: rgba(156, 207, 216, 0.15);
--status-down: #eb6f92;
--status-down-glow: rgba(235, 111, 146, 0.15);
--status-filtered: #f6c177;
--status-filtered-glow: rgba(246, 193, 119, 0.15);
--status-unknown: #6e6a86;
--danger: #eb6f92;
--danger-hover: #db5f82;
}
:root[data-theme="rosepine"] .toolbar-circuit {
filter: hue-rotate(80deg) saturate(0.5) brightness(0.8);
}
/* ===== Theme: Rosé Pine Moon ===== */
:root[data-theme="rosepine-moon"] {
--bg-primary: #232136;
--bg-secondary: #2a273f;
--bg-tertiary: #393552;
--bg-hover: #44415a;
--bg-active: #56526e;
--border: #393552;
--border-bright: #44415a;
--text-primary: #9ccfd8;
--text-secondary: #908caa;
--text-muted: #6e6a86;
--cyan: #c4a7e7;
--cyan-hover: #b498d7;
--cyan-dim: rgba(196, 167, 231, 0.08);
--cyan-glow: rgba(196, 167, 231, 0.2);
--cyan-subtle: rgba(196, 167, 231, 0.03);
--status-up: #9ccfd8;
--status-up-glow: rgba(156, 207, 216, 0.15);
--status-down: #eb6f92;
--status-down-glow: rgba(235, 111, 146, 0.15);
--status-filtered: #f6c177;
--status-filtered-glow: rgba(246, 193, 119, 0.15);
--status-unknown: #6e6a86;
--danger: #eb6f92;
--danger-hover: #db5f82;
}
:root[data-theme="rosepine-moon"] .toolbar-circuit {
filter: hue-rotate(80deg) saturate(0.5) brightness(0.8);
}
/* ===== Theme: Tokyo Night ===== */
:root[data-theme="tokyonight"] {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--bg-tertiary: #1f2335;
--bg-hover: #292e42;
--bg-active: #33384d;
--border: #1f2335;
--border-bright: #292e42;
--text-primary: #bb9af7;
--text-secondary: #a9b1d6;
--text-muted: #565f89;
--cyan: #7aa2f7;
--cyan-hover: #6a92e7;
--cyan-dim: rgba(122, 162, 247, 0.08);
--cyan-glow: rgba(122, 162, 247, 0.2);
--cyan-subtle: rgba(122, 162, 247, 0.03);
--status-up: #9ece6a;
--status-up-glow: rgba(158, 206, 106, 0.15);
--status-down: #f7768e;
--status-down-glow: rgba(247, 118, 142, 0.15);
--status-filtered: #e0af68;
--status-filtered-glow: rgba(224, 175, 104, 0.15);
--status-unknown: #565f89;
--danger: #f7768e;
--danger-hover: #e7667e;
}
:root[data-theme="tokyonight"] .toolbar-circuit {
filter: hue-rotate(40deg) saturate(0.5);
}
/* ===== Theme: Iceberg ===== */
:root[data-theme="iceberg"] {
--bg-primary: #161821;
--bg-secondary: #1e2132;
--bg-tertiary: #252837;
--bg-hover: #33374c;
--bg-active: #3d4259;
--border: #252837;
--border-bright: #33374c;
--text-primary: #c6c8d1;
--text-secondary: #818596;
--text-muted: #444b71;
--cyan: #84a0c6;
--cyan-hover: #7490b6;
--cyan-dim: rgba(132, 160, 198, 0.08);
--cyan-glow: rgba(132, 160, 198, 0.2);
--cyan-subtle: rgba(132, 160, 198, 0.03);
--status-up: #b4be82;
--status-up-glow: rgba(180, 190, 130, 0.15);
--status-down: #e27878;
--status-down-glow: rgba(226, 120, 120, 0.15);
--status-filtered: #e2a478;
--status-filtered-glow: rgba(226, 164, 120, 0.15);
--status-unknown: #444b71;
--danger: #e27878;
--danger-hover: #d26868;
}
:root[data-theme="iceberg"] .toolbar-circuit {
filter: hue-rotate(30deg) saturate(0.4) brightness(0.9);
}
/* ===== Theme: Hacker (Easter Egg) ===== */
:root[data-theme="hacker"] {
@@ -1790,13 +1605,13 @@ body {
display: block;
background:
repeating-linear-gradient(0deg,
rgba(0, 0, 0, 0.5) 0px,
rgba(0, 0, 0, 0.5) 1px,
rgba(0, 0, 0, 0.4) 0px,
rgba(0, 0, 0, 0.4) 1px,
transparent 1px,
transparent 4px),
linear-gradient(180deg,
transparent 0%,
rgba(0, 255, 65, 0.09) 50%,
rgba(0, 255, 65, 0.03) 50%,
transparent 100%);
animation: crt-flicker 3s infinite;
}