swadhinbiswas

[email protected]
2026-07-09 14:00:00 +0600 committed NaN years ago
e086c74
commit e086c74
8 files changed
+265 -0
▼fuzz/.gitignore
+4-0
1
+target
2
+corpus
3
+artifacts
4
+coverage
0
5
▼fuzz/Cargo.toml
+57-0
1
+[package]
2
+name = "justapi-fuzz"
3
+version = "0.1.0"
4
+edition = "2021"
5
+
6
+[package.metadata]
7
+cargo-fuzz = true
8
+
9
+[dependencies]
10
+libfuzzer-sys = "0.4"
11
+justapi-core = { path = "../crates/justapi-core" }
12
+jsonwebtoken = "9"
13
+serde_json = "1"
14
+http = "1"
15
+serde_urlencoded = "0.7"
16
+
17
+[[bin]]
18
+name = "fuzz_router"
19
+path = "fuzz_targets/fuzz_router.rs"
20
+test = false
21
+doc = false
22
+bench = false
23
+
24
+[[bin]]
25
+name = "fuzz_file_paths"
26
+path = "fuzz_targets/fuzz_file_paths.rs"
27
+test = false
28
+doc = false
29
+bench = false
30
+
31
+[[bin]]
32
+name = "fuzz_body"
33
+path = "fuzz_targets/fuzz_body.rs"
34
+test = false
35
+doc = false
36
+bench = false
37
+
38
+[[bin]]
39
+name = "fuzz_query_params"
40
+path = "fuzz_targets/fuzz_query_params.rs"
41
+test = false
42
+doc = false
43
+bench = false
44
+
45
+[[bin]]
46
+name = "fuzz_headers"
47
+path = "fuzz_targets/fuzz_headers.rs"
48
+test = false
49
+doc = false
50
+bench = false
51
+
52
+[[bin]]
53
+name = "fuzz_jwt"
54
+path = "fuzz_targets/fuzz_jwt.rs"
55
+test = false
56
+doc = false
57
+bench = false
0
58
▼fuzz/fuzz_targets/fuzz_body.rs
+25-0
1
+#![no_main]
2
+
3
+use libfuzzer_sys::fuzz_target;
4
+
5
+fuzz_target!(|data: &[u8]| {
6
+ if data.len() < 2 {
7
+ return;
8
+ }
9
+
10
+ // Fuzz JSON body parsing (used by request validation)
11
+ let _: Result<serde_json::Value, _> = serde_json::from_slice(data);
12
+
13
+ // Fuzz JSON Schema validation
14
+ let schema = r#"{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer","minimum":0}},"required":["name"]}"#;
15
+ let _ = justapi_core::validate::validate_json_schema(data, schema);
16
+
17
+ // Fuzz with recursive schema
18
+ let recursive_schema = r##"{"type":"object","additionalProperties":{"$ref":"#"}}"##;
19
+ let _ = justapi_core::validate::validate_json_schema(data, recursive_schema);
20
+
21
+ // Fuzz deeply nested JSON parsing (stack depth)
22
+ if data.len() > 10 {
23
+ let _: Result<serde_json::Value, _> = serde_json::from_slice(data);
24
+ }
25
+});
0
26
▼fuzz/fuzz_targets/fuzz_file_paths.rs
+62-0
1
+#![no_main]
2
+
3
+use libfuzzer_sys::fuzz_target;
4
+use std::path::Path;
5
+
6
+fuzz_target!(|data: &[u8]| {
7
+ if data.is_empty() {
8
+ return;
9
+ }
10
+
11
+ if let Ok(s) = std::str::from_utf8(data) {
12
+ // Sanitize path: prevent directory traversal
13
+ let path = Path::new(s);
14
+ let _ = path.components().collect::<Vec<_>>();
15
+
16
+ // Check for path traversal patterns
17
+ let normalized = s.replace('\\', "/");
18
+ let has_traversal = normalized.contains("..");
19
+ let has_null = s.contains('\0');
20
+ let has_absolute = normalized.starts_with('/');
21
+
22
+ // Simulate path join and canonicalization
23
+ let base = Path::new("/var/www/static");
24
+ let joined = base.join(path);
25
+ let _ = joined.strip_prefix("/var/www/static");
26
+
27
+ // Fuzz MIME type detection by extension
28
+ if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
29
+ let mime = match ext.to_lowercase().as_str() {
30
+ "html" | "htm" => "text/html",
31
+ "css" => "text/css",
32
+ "js" => "application/javascript",
33
+ "json" => "application/json",
34
+ "png" => "image/png",
35
+ "jpg" | "jpeg" => "image/jpeg",
36
+ "gif" => "image/gif",
37
+ "svg" => "image/svg+xml",
38
+ "ico" => "image/x-icon",
39
+ "woff2" => "font/woff2",
40
+ _ => "application/octet-stream",
41
+ };
42
+ let _ = mime;
43
+ }
44
+
45
+ // Fuzz percent-decoding (as used in URL path segments)
46
+ let mut decoded = String::with_capacity(s.len());
47
+ let mut chars = s.chars();
48
+ while let Some(c) = chars.next() {
49
+ if c == '%' {
50
+ let hex: String = chars.by_ref().take(2).collect();
51
+ if hex.len() == 2 {
52
+ if let Ok(byte) = u8::from_str_radix(&hex, 16) {
53
+ decoded.push(byte as char);
54
+ }
55
+ }
56
+ } else {
57
+ decoded.push(c);
58
+ }
59
+ }
60
+ let _ = decoded;
61
+ }
62
+});
0
63
▼fuzz/fuzz_targets/fuzz_headers.rs
+48-0
1
+#![no_main]
2
+
3
+use libfuzzer_sys::fuzz_target;
4
+use http::{HeaderName, HeaderValue, Request, Uri, Method, Version};
5
+
6
+fuzz_target!(|data: &[u8]| {
7
+ if data.is_empty() {
8
+ return;
9
+ }
10
+
11
+ // Try to parse as raw header name+value pairs
12
+ if let Ok(s) = std::str::from_utf8(data) {
13
+ for line in s.lines().take(10) {
14
+ if let Some(pos) = line.find(':') {
15
+ let name_str = &line[..pos].trim();
16
+ let val_str = &line[pos + 1..].trim();
17
+ let _ = HeaderName::from_bytes(name_str.as_bytes());
18
+ let _ = HeaderValue::from_bytes(val_str.as_bytes());
19
+ }
20
+ }
21
+ }
22
+
23
+ // Fuzz HeaderName directly
24
+ let _ = HeaderName::from_bytes(data);
25
+
26
+ // Fuzz HeaderValue directly
27
+ let _ = HeaderValue::from_bytes(data);
28
+
29
+ // Fuzz URI parsing
30
+ if let Ok(s) = std::str::from_utf8(data) {
31
+ let _: Result<Uri, _> = s.parse();
32
+ }
33
+
34
+ // Fuzz Method parsing
35
+ if let Ok(s) = std::str::from_utf8(data) {
36
+ let _: Result<Method, _> = s.parse();
37
+ }
38
+
39
+ // Fuzz Version parsing
40
+ let _ = match data.first().copied().unwrap_or(0) {
41
+ 0 => Version::HTTP_09,
42
+ 1 => Version::HTTP_10,
43
+ 2 => Version::HTTP_11,
44
+ 3 => Version::HTTP_2,
45
+ 4 => Version::HTTP_3,
46
+ _ => Version::HTTP_11,
47
+ };
48
+});
0
49
▼fuzz/fuzz_targets/fuzz_jwt.rs
+22-0
1
+#![no_main]
2
+
3
+use libfuzzer_sys::fuzz_target;
4
+use jsonwebtoken::{decode, decode_header, Validation, DecodingKey, Algorithm};
5
+
6
+fuzz_target!(|data: &[u8]| {
7
+ if data.len() < 4 {
8
+ return;
9
+ }
10
+ let key = DecodingKey::from_secret(b"test-secret-key-for-fuzzing");
11
+ let validation = Validation::new(Algorithm::HS256);
12
+
13
+ if let Ok(s) = std::str::from_utf8(data) {
14
+ let _ = decode_header(s);
15
+ let _ = decode::<serde_json::Value>(s, &key, &validation);
16
+ }
17
+
18
+ let alt_key = DecodingKey::from_secret(data);
19
+ if let Ok(s) = std::str::from_utf8(data) {
20
+ let _ = decode::<serde_json::Value>(s, &alt_key, &validation);
21
+ }
22
+});
0
23
▼fuzz/fuzz_targets/fuzz_query_params.rs
+21-0
1
+#![no_main]
2
+
3
+use libfuzzer_sys::fuzz_target;
4
+use std::collections::HashMap;
5
+
6
+fuzz_target!(|data: &[u8]| {
7
+ if data.is_empty() {
8
+ return;
9
+ }
10
+
11
+ if let Ok(s) = std::str::from_utf8(data) {
12
+ // Fuzz URL query string parsing
13
+ let _: HashMap<String, String> = serde_urlencoded::from_str(s).unwrap_or_default();
14
+
15
+ // Fuzz full URL parsing with query parameters
16
+ if s.len() > 4 && s.contains('=') {
17
+ let url = format!("http://localhost:8080/test?{}", s);
18
+ let _: Result<http::Uri, _> = url.parse();
19
+ }
20
+ }
21
+});
0
22
▼fuzz/fuzz_targets/fuzz_router.rs
+26-0
1
+#![no_main]
2
+
3
+use libfuzzer_sys::fuzz_target;
4
+
5
+fuzz_target!(|data: &[u8]| {
6
+ if data.is_empty() {
7
+ return;
8
+ }
9
+
10
+ if let Ok(s) = std::str::from_utf8(data) {
11
+ // Build a router with sample routes and fuzz the path matching
12
+ use http::Method;
13
+ let mut router = justapi_core::router::Router::new();
14
+ let _ = router.insert(Method::GET, "/users/{id}", 1usize);
15
+ let _ = router.insert(Method::GET, "/users/{id}/posts", 2);
16
+ let _ = router.insert(Method::GET, "/users/{id}/posts/{post_id}", 3);
17
+ let _ = router.insert(Method::GET, "/static/{*path}", 4);
18
+ let _ = router.insert(Method::GET, "/api/v1/{resource}", 5);
19
+ let _ = router.insert(Method::GET, "/api/v1/{resource}/{action}", 6);
20
+ let _ = router.insert(Method::GET, "/health", 7);
21
+ let _ = router.insert(Method::GET, "/live", 8);
22
+ let _ = router.insert(Method::GET, "/ready", 9);
23
+
24
+ let _ = router.at(&Method::GET, s);
25
+ }
26
+});
0
27