Skip to content

Commit ae6261a

Browse files
committed
feat: some updates
1 parent 94eb03b commit ae6261a

13 files changed

Lines changed: 1653 additions & 1915 deletions

File tree

Cargo.lock

Lines changed: 1201 additions & 1347 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,36 @@ cli = ["clap", "colored"]
2020
gui = ["iced", "rfd", "tokio"]
2121

2222
[dependencies]
23-
regex = "1.12.2"
24-
zip = "6.0.0"
25-
indicatif = "0.18.3"
23+
regex = "1.12.3"
24+
zip = "8.1.0"
25+
indicatif = "0.18.4"
2626
lazy_static = "1.5.0"
27-
url = "2.5.7"
27+
url = "2.5.8"
2828
hex = "0.4.3"
2929
byteorder = "1.5.0"
3030
walkdir = "2.5.0"
3131
serde = { version = "1.0.228", features = ["derive"] }
32-
serde_json = "1.0.145"
33-
tempfile = "3.23.0"
34-
thiserror = "2.0.17"
32+
serde_json = "1.0.149"
33+
tempfile = "3.26.0"
34+
thiserror = "2.0.18"
3535
rayon = "1.11.0"
3636
encoding_rs = "0.8.35"
37-
wildmatch = "2.6.0"
38-
lru = "0.16.2"
39-
sysinfo = "0.37.2"
37+
wildmatch = "2.6.1"
38+
lru = "0.16.3"
39+
sysinfo = "0.38.2"
4040
bloomfilter = "3.0.1"
4141
ipnet = "2.11.0"
42-
moka = { version = "0.12.11", features = ["sync"] }
43-
memmap2 = "0.9.9"
42+
moka = { version = "0.12.13", features = ["sync"] }
43+
memmap2 = "0.9.10"
4444

4545
# CLI dependencies (optional)
46-
colored = { version = "3.0.0", optional = true }
47-
clap = { version = "4.5.51", features = ["derive"], optional = true }
46+
colored = { version = "3.1.1", optional = true }
47+
clap = { version = "4.5.60", features = ["derive"], optional = true }
4848

4949
# GUI dependencies (optional)
50-
iced = { version = "0.13.1", features = ["tokio", "svg"], optional = true }
51-
rfd = { version = "0.15.4", optional = true }
52-
tokio = { version = "1.48.0", features = ["full"], optional = true }
50+
iced = { version = "0.14.0", features = ["tokio", "svg"], optional = true }
51+
rfd = { version = "0.17.2", optional = true }
52+
tokio = { version = "1.49.0", features = ["full"], optional = true }
5353

5454
[profile.release]
5555
codegen-units = 1

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ collapsescanner file.jar --ignore_keywords ignore_keywords.txt
9494
| `--parallel-scanning` | Force-enable parallel scanning (overrides automatic decision) |
9595
| `--no-parallel-scanning` | Force-disable parallel scanning (explicitly disable) |
9696
| `--available-memory-mb` | Override detected available memory (MB) for tuning/testing |
97-
| `--show` | Print a detailed findings report to the terminal (useful for interactive runs) |
9897
| `--max_file_size` | Maximum file size to scan (in MB). Files larger than this will be skipped. |
9998

10099
## 🛡️ Detection Capabilities

src/config.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ use std::env;
33
use sysinfo::System;
44

55
const DEFAULT_RESULT_CACHE_SIZE: usize = 4096;
6-
const DEFAULT_BUFFER_SIZE: usize = 512 * 1024;
6+
const DEFAULT_BUFFER_SIZE: usize = 512 * 1024; // 512 KB
77
const DEFAULT_SAFE_STRING_CACHE_CAPACITY: usize = 4000;
88

9-
const LOW_MEMORY_THRESHOLD: u64 = 4 * 1024 * 1024 * 1024;
10-
const MEDIUM_MEMORY_THRESHOLD: u64 = 8 * 1024 * 1024 * 1024;
11-
const HIGH_MEMORY_THRESHOLD: u64 = 16 * 1024 * 1024 * 1024;
9+
const LOW_MEMORY_THRESHOLD: u64 = 4 * 1024 * 1024 * 1024; // 4 GB
10+
const MEDIUM_MEMORY_THRESHOLD: u64 = 8 * 1024 * 1024 * 1024; // 8 GB
11+
const HIGH_MEMORY_THRESHOLD: u64 = 16 * 1024 * 1024 * 1024; // 16 GB
1212

1313
lazy_static! {
1414
pub static ref SYSTEM_CONFIG: SystemConfig = SystemConfig::new();
@@ -63,9 +63,9 @@ impl SystemConfig {
6363
} else {
6464
match available_memory {
6565
mem if mem < LOW_MEMORY_THRESHOLD => DEFAULT_BUFFER_SIZE,
66-
mem if mem < MEDIUM_MEMORY_THRESHOLD => 2 * 1024 * 1024,
67-
mem if mem < HIGH_MEMORY_THRESHOLD => 8 * 1024 * 1024,
68-
_ => 16 * 1024 * 1024,
66+
mem if mem < MEDIUM_MEMORY_THRESHOLD => 2 * 1024 * 1024, // 2 MB
67+
mem if mem < HIGH_MEMORY_THRESHOLD => 8 * 1024 * 1024, // 8 MB
68+
_ => 16 * 1024 * 1024, // 16 MB
6969
}
7070
};
7171

src/detection.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use std::collections::hash_map::DefaultHasher;
12
use std::collections::HashSet;
3+
use std::hash::{Hash, Hasher};
24

35
lazy_static::lazy_static! {
46
pub static ref SAFE_STRING_CACHE: moka::sync::Cache<String, ()> = {
@@ -34,8 +36,9 @@ pub fn is_cached_safe_string(s: &str) -> bool {
3436
return true;
3537
}
3638

39+
let string_owned = s.to_owned();
3740
if let Ok(bloom_guard) = SAFE_STRING_BLOOM.try_read() {
38-
if !bloom_guard.check(&s.to_string()) {
41+
if !bloom_guard.check(&string_owned) {
3942
return false;
4043
}
4144
} else {
@@ -59,9 +62,6 @@ pub fn cache_safe_string(s: &str) -> bool {
5962
}
6063

6164
pub fn calculate_detection_hash(data: &[u8]) -> u64 {
62-
use std::collections::hash_map::DefaultHasher;
63-
use std::hash::{Hash, Hasher};
64-
6565
let mut hasher = DefaultHasher::new();
6666

6767
if data.len() > 1024 {

src/filters.rs

Lines changed: 82 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,54 @@ lazy_static::lazy_static! {
1616
\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»""']))"#).unwrap();
1717

1818
/// Generic malicious / suspicious pattern keywords
19-
pub static ref MALICIOUS_PATTERN_REGEX: Regex = Regex::new(r"(?i)\b(powershell|cmd\.exe|Runtime\.getRuntime\(\)\.exec|ProcessBuilder|loadLibrary|socket\(|bind\(|connect\(|URL\(|URLConnection|Class\.forName|defineClass|getMethod|ldap|rmi)\b").unwrap();
19+
pub static ref MALICIOUS_PATTERN_REGEX: Regex = Regex::new(r"(?i)\b(powershell|cmd\.exe|Runtime\.getRuntime\(\)\.exec)\b").unwrap();
2020

2121
/// Known "good" links / domains
22-
pub static ref GOOD_LINKS: Vec<String> = vec![
23-
"account.mojang.com".to_string(),
24-
"aka.ms".to_string(),
25-
"apache.org".to_string(),
26-
"api.mojang.com".to_string(),
27-
"api.spiget.org".to_string(),
28-
"authserver.mojang.com".to_string(),
29-
"bugs.mojang.com".to_string(),
30-
"cabaletta/baritone".to_string(),
31-
"ci.viaversion.com".to_string(),
32-
"com/viaversion/".to_string(),
33-
"docs.advntr.dev".to_string(),
34-
"dominos.com".to_string(),
35-
"dump.viaversion.com".to_string(),
36-
"eclipse.org".to_string(),
37-
"java.sun.org".to_string(),
38-
"jo0001.github.io".to_string(),
39-
"logging.apache.org".to_string(),
40-
"login.live.com".to_string(),
41-
"lwjgl.org".to_string(),
42-
"minecraft.net".to_string(),
43-
"minecraft.org".to_string(),
44-
"minotar.net".to_string(),
45-
"mojang.com".to_string(),
46-
"netty.io".to_string(),
47-
"optifine.net".to_string(),
48-
"paulscode/sound/".to_string(),
49-
"s.optifine.net".to_string(),
50-
"sessionserver.mojang.com".to_string(),
51-
"shader-tutorial.dev".to_string(),
52-
"snoop.minecraft.net".to_string(),
53-
"tools.ietf.org".to_string(),
54-
"viaversion.com".to_string(),
55-
"www.openssl.org".to_string(),
56-
"www.rfc-editor.org".to_string(),
57-
"www.slf4j.org".to_string(),
58-
"www.w3.org".to_string(),
59-
"yaml.org".to_string(),
60-
"openssl.org".to_string(),
61-
"yggdrasil-auth-session-staging.mojang.zone".to_string(),
62-
"slf4j.org".to_string(),
63-
];
22+
pub static ref GOOD_LINKS: HashSet<String> = [
23+
"account.mojang.com",
24+
"aka.ms",
25+
"apache.org",
26+
"api.mojang.com",
27+
"api.spiget.org",
28+
"authserver.mojang.com",
29+
"bugs.mojang.com",
30+
"cabaletta/baritone",
31+
"ci.viaversion.com",
32+
"com/viaversion/",
33+
"docs.advntr.dev",
34+
"dominos.com",
35+
"dump.viaversion.com",
36+
"eclipse.org",
37+
"java.sun.org",
38+
"jo0001.github.io",
39+
"logging.apache.org",
40+
"login.live.com",
41+
"lwjgl.org",
42+
"minecraft.net",
43+
"minecraft.org",
44+
"minotar.net",
45+
"mojang.com",
46+
"netty.io",
47+
"optifine.net",
48+
"paulscode/sound/",
49+
"s.optifine.net",
50+
"sessionserver.mojang.com",
51+
"shader-tutorial.dev",
52+
"snoop.minecraft.net",
53+
"tools.ietf.org",
54+
"viaversion.com",
55+
"www.openssl.org",
56+
"www.rfc-editor.org",
57+
"www.slf4j.org",
58+
"www.w3.org",
59+
"yaml.org",
60+
"openssl.org",
61+
"yggdrasil-auth-session-staging.mojang.zone",
62+
"slf4j.org",
63+
]
64+
.into_iter()
65+
.map(str::to_owned)
66+
.collect();
6467

6568
/// Known "good" / unreachable / reserved IPs and ranges
6669
pub static ref GOOD_IPS: HashSet<&'static str> = {
@@ -140,3 +143,39 @@ pub fn is_known_good_ip(ip: &str) -> bool {
140143
}
141144
false
142145
}
146+
147+
pub fn is_public_routable_ip(ip: &str) -> bool {
148+
match ip.parse::<IpAddr>() {
149+
Ok(addr) => {
150+
if is_known_good_ip(ip) {
151+
return false;
152+
}
153+
154+
match addr {
155+
IpAddr::V4(v4) => {
156+
!(v4.is_private()
157+
|| v4.is_loopback()
158+
|| v4.is_link_local()
159+
|| v4.is_broadcast()
160+
|| v4.is_documentation()
161+
|| v4.is_multicast()
162+
|| v4.is_unspecified())
163+
}
164+
IpAddr::V6(v6) => {
165+
let segments = v6.segments();
166+
let is_site_local = (segments[0] & 0xffc0) == 0xfec0;
167+
let is_documentation = segments[0] == 0x2001 && segments[1] == 0x0db8;
168+
169+
!(v6.is_loopback()
170+
|| v6.is_unspecified()
171+
|| v6.is_unique_local()
172+
|| v6.is_unicast_link_local()
173+
|| is_site_local
174+
|| is_documentation
175+
|| v6.is_multicast())
176+
}
177+
}
178+
}
179+
Err(_) => false,
180+
}
181+
}

src/main.rs

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,6 @@ struct Args {
8989

9090
#[clap(long, value_parser)]
9191
max_file_size: Option<usize>,
92-
93-
#[clap(long, action = clap::ArgAction::SetTrue)]
94-
show: bool,
9592
}
9693

9794
#[cfg(all(feature = "cli", not(feature = "gui")))]
@@ -482,78 +479,11 @@ fn run_cli() -> Result<(), Box<dyn std::error::Error>> {
482479

483480
for result in &sorted_significant_results {
484481
for (finding_type, _) in result.matches.iter() {
485-
*findings_by_type.entry(finding_type.clone()).or_insert(0) += 1;
482+
*findings_by_type.entry(*finding_type).or_insert(0) += 1;
486483
total_findings += 1;
487484
}
488485
}
489486

490-
if args.show {
491-
println!(
492-
"\n{}",
493-
"╔══════════════════════════════════════════════════════════════════════════════╗"
494-
.bright_blue()
495-
.bold()
496-
);
497-
println!(
498-
"{}",
499-
"║ FINDINGS REPORT ║"
500-
.bright_blue()
501-
.bold()
502-
);
503-
println!(
504-
"{}",
505-
"╚══════════════════════════════════════════════════════════════════════════════╝"
506-
.bright_blue()
507-
.bold()
508-
);
509-
510-
let mut findings_by_type: HashMap<FindingType, Vec<(String, String)>> =
511-
HashMap::new();
512-
513-
for result in &sorted_significant_results {
514-
for (finding_type, value) in result.matches.iter() {
515-
findings_by_type
516-
.entry(finding_type.clone())
517-
.or_default()
518-
.push((result.file_path.clone(), value.clone()));
519-
}
520-
}
521-
522-
let mut finding_types: Vec<FindingType> =
523-
findings_by_type.keys().cloned().collect();
524-
finding_types.sort_by_key(|t| t.to_string());
525-
526-
for ftype in &finding_types {
527-
let entries = findings_by_type.get(ftype).unwrap();
528-
let (icon, color) = ftype.with_emoji();
529-
530-
println!(
531-
"\n {} {} ({})",
532-
icon.color(color).bold(),
533-
ftype.to_string().color(color).bold(),
534-
entries.len().to_string().bright_white()
535-
);
536-
537-
let mut sorted_entries = entries.clone();
538-
sorted_entries.sort_by(|a, b| {
539-
let file_cmp = a.0.cmp(&b.0);
540-
if file_cmp == std::cmp::Ordering::Equal {
541-
a.1.cmp(&b.1)
542-
} else {
543-
file_cmp
544-
}
545-
});
546-
547-
for (file_path, value) in sorted_entries {
548-
println!(
549-
" • {}: {}",
550-
file_path.bright_cyan(),
551-
value.bright_white()
552-
);
553-
}
554-
}
555-
}
556-
557487
println!(
558488
"\n{}",
559489
"╔══════════════════════════════════════════════════════════════════════════════╗"
@@ -621,7 +551,7 @@ fn run_cli() -> Result<(), Box<dyn std::error::Error>> {
621551
for result in &sorted_significant_results {
622552
for (finding_type, value) in result.matches.iter() {
623553
all_findings
624-
.entry(finding_type.clone())
554+
.entry(*finding_type)
625555
.or_default()
626556
.insert(value.clone());
627557
}
@@ -640,26 +570,10 @@ fn run_cli() -> Result<(), Box<dyn std::error::Error>> {
640570
let mut sorted_values: Vec<&String> = values.iter().collect();
641571
sorted_values.sort();
642572

643-
for (i, value) in sorted_values.iter().enumerate() {
644-
if i < 5 {
645-
println!(" • {}", value.bright_white());
646-
} else if i == 5 {
647-
println!(
648-
" • ... and {} more",
649-
(values.len() - 5).to_string().dimmed()
650-
);
651-
break;
652-
}
573+
for value in sorted_values.iter() {
574+
println!(" • {}", value.bright_white());
653575
}
654576
}
655-
656-
if !args.show {
657-
println!(
658-
"\n{} {}",
659-
"💡".cyan().bold(),
660-
"Tip: Use the '--show' flag to display detailed findings.".cyan()
661-
);
662-
}
663577
} else {
664578
let scan_duration = scan_start_time.elapsed();
665579
let total_files_scanned = results.len();

0 commit comments

Comments
 (0)