5 Commits
Author SHA1 Message Date
mysticmomba cf2cc9f8ed Added build script 2026-10-09 09:46:32 -04:00
mysticmomba fd8bb16d40 Updated Theme 2026-10-09 09:38:24 -04:00
mysticmomba ed9ed59de9 Updated themes a tad 2026-10-08 15:12:29 -04:00
mysticmomba 56038b7a7e feat: Added more themes 2026-10-08 12:50:48 -04:00
mysticmomba 7a8465b7fb Updated License 2026-10-08 12:25:34 -04:00
16 changed files with 251 additions and 46 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. the "copyright" line and a pointer to where the full notice is found.
Argus, an all in one network utility Argus, an all in one network utility
Copyright (C) 2026 Jamse Brotosky Copyright (C) 2026 James Brotosky
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU Affero General Public License as published
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/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
+3 -4
View File
@@ -1,12 +1,11 @@
use pulldown_cmark::{Options, Parser, html}; use pulldown_cmark::{html, Options, Parser};
const CHANGELOG_MD: &str = include_str!("../../../CHANGELOG.md"); const CHANGELOG_MD: &str = include_str!("../../../CHANGELOG.md");
#[tauri::command] #[tauri::command]
pub async fn get_changelog() -> Result<String, String> { pub async fn get_changelog() -> Result<String, String> {
let options = Options::ENABLE_HEADING_ATTRIBUTES let options =
| Options::ENABLE_STRIKETHROUGH Options::ENABLE_HEADING_ATTRIBUTES | Options::ENABLE_STRIKETHROUGH | Options::ENABLE_TABLES;
| Options::ENABLE_TABLES;
let parser = Parser::new_ext(CHANGELOG_MD, options); let parser = Parser::new_ext(CHANGELOG_MD, options);
let mut html_output = String::new(); let mut html_output = String::new();
+1 -2
View File
@@ -11,8 +11,7 @@ pub struct ExportRequest {
#[tauri::command] #[tauri::command]
pub async fn export_csv(request: ExportRequest) -> Result<(), String> { pub async fn export_csv(request: ExportRequest) -> Result<(), String> {
let file = File::create(&request.path) let file = File::create(&request.path).map_err(|e| format!("Failed to create file: {}", e))?;
.map_err(|e| format!("Failed to create file: {}", e))?;
let mut writer = csv::Writer::from_writer(file); let mut writer = csv::Writer::from_writer(file);
+10 -1
View File
@@ -40,7 +40,16 @@ pub async fn start_scan(app: AppHandle, config: ScanConfig) -> Result<(), String
let port_timeout_ms = config.port_timeout_ms.unwrap_or(1000); let port_timeout_ms = config.port_timeout_ms.unwrap_or(1000);
tokio::spawn(async move { 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(()) Ok(())
} }
+2 -2
View File
@@ -6,7 +6,7 @@ pub enum HostStatus {
Up, Up,
Down, Down,
Filtered, Filtered,
Unknown Unknown,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -42,7 +42,7 @@ pub struct PortResult {
pub struct NetbiosInfo { pub struct NetbiosInfo {
pub computer_name: Option<String>, pub computer_name: Option<String>,
pub workgroup: Option<String>, pub workgroup: Option<String>,
pub mac_from_netbios: Option<String> pub mac_from_netbios: Option<String>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
+1 -3
View File
@@ -3,9 +3,7 @@ use std::net::IpAddr;
/// Perform reverse DNS lookup on a single IP. /// Perform reverse DNS lookup on a single IP.
/// Returns the hostname if found, None otherwise. /// Returns the hostname if found, None otherwise.
pub async fn reverse_lookup(ip: IpAddr) -> Option<String> { pub async fn reverse_lookup(ip: IpAddr) -> Option<String> {
let result = tokio::task::spawn_blocking(move || { let result = tokio::task::spawn_blocking(move || dns_lookup::lookup_addr(&ip)).await;
dns_lookup::lookup_addr(&ip)
}).await;
match result { match result {
Ok(Ok(hostname)) => { Ok(Ok(hostname)) => {
+1 -1
View File
@@ -1,2 +1,2 @@
pub mod range;
pub mod dns; pub mod dns;
pub mod range;
+1 -1
View File
@@ -1,3 +1,3 @@
pub mod netbios;
pub mod ping; pub mod ping;
pub mod ports; pub mod ports;
pub mod netbios;
+1 -2
View File
@@ -91,8 +91,7 @@ pub async fn ping_sweep(
let nb_fut = query_netbios(enriched.ip, 500); let nb_fut = query_netbios(enriched.ip, 500);
if port_scan { if port_scan {
let scan_ports = ports2 let scan_ports = ports2.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
let (nb_result, port_results) = tokio::join!( let (nb_result, port_results) = tokio::join!(
nb_fut, nb_fut,
scan_host_ports(enriched.ip, &scan_ports, port_timeout_ms) scan_host_ports(enriched.ip, &scan_ports, port_timeout_ms)
+15 -10
View File
@@ -42,8 +42,7 @@ fn service_name(port: u16) -> Option<String> {
} }
pub const DEFAULT_PORTS: &[u16] = &[ pub const DEFAULT_PORTS: &[u16] = &[
21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 21, 22, 23, 25, 53, 80, 110, 111, 135, 139, 143, 443, 445, 993, 995, 1433, 1521, 3306, 3389,
443, 445, 993, 995, 1433, 1521, 3306, 3389,
5432, 5900, 5985, 5986, 8080, 8088, 8443, 5432, 5900, 5985, 5986, 8080, 8088, 8443,
]; ];
@@ -96,7 +95,10 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
}; };
if let Some(data) = probe { 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; return None;
} }
} }
@@ -107,8 +109,15 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
Ok(Ok(n)) if n > 0 => { Ok(Ok(n)) if n > 0 => {
buf.truncate(n); buf.truncate(n);
// Strip to printable ASCII, replace control chars // Strip to printable ASCII, replace control chars
let banner: String = buf.iter() let banner: String = buf
.map(|&b| if b >= 0x20 && b < 0x7f { b as char } else { ' ' }) .iter()
.map(|&b| {
if (0x20..0x7f).contains(&b) {
b as char
} else {
' '
}
})
.collect(); .collect();
let banner = banner.trim().to_string(); let banner = banner.trim().to_string();
if banner.is_empty() { if banner.is_empty() {
@@ -124,11 +133,7 @@ async fn grab_banner(mut stream: TcpStream, port: u16) -> Option<String> {
const MAX_PORT_CONCURRENT: usize = 64; const MAX_PORT_CONCURRENT: usize = 64;
pub async fn scan_host_ports( pub async fn scan_host_ports(ip: IpAddr, ports: &[u16], timeout_ms: u64) -> Vec<PortResult> {
ip: IpAddr,
ports: &[u16],
timeout_ms: u64,
) -> Vec<PortResult> {
let mut results = Vec::with_capacity(ports.len()); let mut results = Vec::with_capacity(ports.len());
for chunk in ports.chunks(MAX_PORT_CONCURRENT) { for chunk in ports.chunks(MAX_PORT_CONCURRENT) {
+5
View File
@@ -211,6 +211,11 @@
<option value="nord">Nord</option> <option value="nord">Nord</option>
<option value="solarized">Solarized Dark</option> <option value="solarized">Solarized Dark</option>
<option value="mocha">Catppuccin Mocha</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> </select>
<span class="status-bar-item" id="privilege-status">unprivileged</span> <span class="status-bar-item" id="privilege-status">unprivileged</span>
</div> </div>
+195 -10
View File
@@ -1456,7 +1456,7 @@ body {
--border: #0d4450; --border: #0d4450;
--border-bright: #1a5c6a; --border-bright: #1a5c6a;
--text-primary: #93a1a1; --text-primary: #cb4b16;
--text-secondary: #839496; --text-secondary: #839496;
--text-muted: #586e75; --text-muted: #586e75;
@@ -1489,11 +1489,11 @@ body {
--border: #313244; --border: #313244;
--border-bright: #45475a; --border-bright: #45475a;
--text-primary: #cdd6f4; --text-primary: #94e2d5;
--text-secondary: #a6adc8; --text-secondary: #a6adc8;
--text-muted: #6c7086; --text-muted: #6c7086;
--cyan: #89b4fa; --cyan: #b4befe;
--cyan-hover: #7ba4eb; --cyan-hover: #7ba4eb;
--cyan-dim: rgba(137, 180, 250, 0.08); --cyan-dim: rgba(137, 180, 250, 0.08);
--cyan-glow: rgba(137, 180, 250, 0.2); --cyan-glow: rgba(137, 180, 250, 0.2);
@@ -1515,24 +1515,209 @@ body {
filter: hue-rotate(40deg) saturate(0.6) brightness(0.9); filter: hue-rotate(40deg) saturate(0.6) brightness(0.9);
} }
:root[data-theme="latte"] .port-tag { :root[data-theme="mocha"] .port-tag {
background: rgba(30, 102, 245, 0.1); background: rgba(30, 102, 245, 0.1);
border-color: rgba(30, 102, 245, 0.2); border-color: rgba(30, 102, 245, 0.2);
} }
:root[data-theme="latte"] #privilege-status::before { :root[data-theme="mocha"] #privilege-status::before {
background: var(--status-up); background: var(--status-up);
box-shadow: 0 0 4px rgba(64, 160, 43, 0.3); box-shadow: 0 0 4px rgba(64, 160, 43, 0.3);
} }
:root[data-theme="latte"] ::-webkit-scrollbar-track { :root[data-theme="mocha"] ::-webkit-scrollbar-track {
background: var(--bg-primary); background: var(--bg-primary);
} }
:root[data-theme="latte"] ::-webkit-scrollbar-thumb { :root[data-theme="mocha"] ::-webkit-scrollbar-thumb {
background: var(--border-bright); 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) ===== */ /* ===== Theme: Hacker (Easter Egg) ===== */
:root[data-theme="hacker"] { :root[data-theme="hacker"] {
@@ -1605,13 +1790,13 @@ body {
display: block; display: block;
background: background:
repeating-linear-gradient(0deg, repeating-linear-gradient(0deg,
rgba(0, 0, 0, 0.4) 0px, rgba(0, 0, 0, 0.5) 0px,
rgba(0, 0, 0, 0.4) 1px, rgba(0, 0, 0, 0.5) 1px,
transparent 1px, transparent 1px,
transparent 4px), transparent 4px),
linear-gradient(180deg, linear-gradient(180deg,
transparent 0%, transparent 0%,
rgba(0, 255, 65, 0.03) 50%, rgba(0, 255, 65, 0.09) 50%,
transparent 100%); transparent 100%);
animation: crt-flicker 3s infinite; animation: crt-flicker 3s infinite;
} }