thuban 0.4.11 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/package.json +1 -1
- package/dist/packages/crucible/rules/code-scanner-core.json +11 -0
- package/dist/packages/scanner/ghost-code-detector.js +1 -1
- package/package.json +1 -1
- package/dist/packages/crucible/seeds/python/seed-021 2.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-022 2.py +0 -34
- package/dist/packages/crucible/seeds/python/seed-023 2.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-024 2.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-025 2.py +0 -40
- package/dist/packages/crucible/seeds/python/seed-026 2.py +0 -35
- package/dist/packages/crucible/seeds/python/seed-027 2.py +0 -35
- package/dist/packages/crucible/seeds/python/seed-028 2.py +0 -42
- package/dist/packages/crucible/seeds/python/seed-030 2.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-031 2.py +0 -34
- package/dist/packages/crucible/seeds/python/seed-036 2.py +0 -33
- package/dist/packages/crucible/seeds/python/seed-037 2.py +0 -41
- package/dist/packages/crucible/seeds/python/seed-038 2.py +0 -33
- package/dist/packages/crucible/seeds/python/seed-039 2.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-040 2.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-041 2.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-042 2.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-043 2.py +0 -32
- package/dist/packages/crucible/seeds/python/seed-044 2.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-045 2.py +0 -36
- package/dist/packages/crucible/seeds/python/seed-046 2.py +0 -33
- package/dist/packages/crucible/seeds/python/seed-047 2.py +0 -44
- package/dist/packages/crucible/seeds/python/seed-048 2.py +0 -35
- package/dist/packages/crucible/seeds/python/seed-049 2.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-050 2.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-051 2.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-052 2.py +0 -41
- package/dist/packages/crucible/seeds/ruby/seed-001 2.rb +0 -15
- package/dist/packages/crucible/seeds/ruby/seed-002 2.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-003 2.rb +0 -25
- package/dist/packages/crucible/seeds/ruby/seed-004 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-005 2.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-006 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-007 2.rb +0 -16
- package/dist/packages/crucible/seeds/ruby/seed-008 2.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-009 2.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-010 2.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-011 2.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-012 2.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-013 2.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-014 2.rb +0 -16
- package/dist/packages/crucible/seeds/ruby/seed-015 2.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-016 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-017 2.rb +0 -25
- package/dist/packages/crucible/seeds/ruby/seed-018 2.rb +0 -23
- package/dist/packages/crucible/seeds/ruby/seed-019 2.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-020 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-021 2.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-022 2.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-023 2.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-024 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-025 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-026 2.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-027 2.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-028 2.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-029 2.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-030 2.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-031 2.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-032 2.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-033 2.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-034 2.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-035 2.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-036 2.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-037 2.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-038 2.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-039 2.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-040 2.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-041 2.rb +0 -23
- package/dist/packages/crucible/seeds/ruby/seed-042 2.rb +0 -25
- package/dist/packages/crucible/seeds/ruby/seed-043 2.rb +0 -23
- package/dist/packages/crucible/seeds/ruby/seed-044 2.rb +0 -16
- package/dist/packages/crucible/seeds/ruby/seed-045 2.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-046 2.rb +0 -27
- package/dist/packages/crucible/seeds/ruby/seed-047 2.rb +0 -26
- package/dist/packages/crucible/seeds/ruby/seed-048 2.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-049 2.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-050 2.rb +0 -27
- package/dist/packages/crucible/seeds/rust/seed-001 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-002 2.rs +0 -20
- package/dist/packages/crucible/seeds/rust/seed-003 2.rs +0 -23
- package/dist/packages/crucible/seeds/rust/seed-004 2.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-005 2.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-006 2.rs +0 -24
- package/dist/packages/crucible/seeds/rust/seed-007 2.rs +0 -20
- package/dist/packages/crucible/seeds/rust/seed-008 2.rs +0 -19
- package/dist/packages/crucible/seeds/rust/seed-009 2.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-010 2.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-011 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-012 2.rs +0 -31
- package/dist/packages/crucible/seeds/rust/seed-013 2.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-014 2.rs +0 -30
- package/dist/packages/crucible/seeds/rust/seed-015 2.rs +0 -33
- package/dist/packages/crucible/seeds/rust/seed-016 2.rs +0 -22
- package/dist/packages/crucible/seeds/rust/seed-017 2.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-018 2.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-019 2.rs +0 -36
- package/dist/packages/crucible/seeds/rust/seed-020 2.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-021 2.rs +0 -26
- package/dist/packages/crucible/seeds/rust/seed-022 2.rs +0 -23
- package/dist/packages/crucible/seeds/rust/seed-023 2.rs +0 -22
- package/dist/packages/crucible/seeds/rust/seed-024 2.rs +0 -24
- package/dist/packages/crucible/seeds/rust/seed-025 2.rs +0 -29
- package/dist/packages/crucible/seeds/rust/seed-026 2.rs +0 -23
- package/dist/packages/crucible/seeds/rust/seed-027 2.rs +0 -24
- package/dist/packages/crucible/seeds/rust/seed-028 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-029 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-030 2.rs +0 -30
- package/dist/packages/crucible/seeds/rust/seed-031 2.rs +0 -22
- package/dist/packages/crucible/seeds/rust/seed-032 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-033 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-034 2.rs +0 -20
- package/dist/packages/crucible/seeds/rust/seed-035 2.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-036 2.rs +0 -26
- package/dist/packages/crucible/seeds/rust/seed-037 2.rs +0 -31
- package/dist/packages/crucible/seeds/rust/seed-038 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-039 2.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-040 2.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-041 2.rs +0 -32
- package/dist/packages/crucible/seeds/rust/seed-042 2.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-043 2.rs +0 -29
- package/dist/packages/crucible/seeds/rust/seed-044 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-045 2.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-046 2.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-047 2.rs +0 -34
- package/dist/packages/crucible/seeds/rust/seed-048 2.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-049 2.rs +0 -26
- package/dist/packages/crucible/seeds/rust/seed-050 2.rs +0 -23
- package/dist/packages/crucible/seeds/ts/seed-001 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-002 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-003 2.ts +0 -28
- package/dist/packages/crucible/seeds/ts/seed-004 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-005 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-006 2.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-007 2.ts +0 -28
- package/dist/packages/crucible/seeds/ts/seed-008 2.ts +0 -40
- package/dist/packages/crucible/seeds/ts/seed-009 2.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-010 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-011 2.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-012 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-013 2.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-014 2.ts +0 -36
- package/dist/packages/crucible/seeds/ts/seed-015 2.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-016 2.ts +0 -37
- package/dist/packages/crucible/seeds/ts/seed-017 2.ts +0 -44
- package/dist/packages/crucible/seeds/ts/seed-018 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-019 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-020 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-021 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-022 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-023 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-024 2.ts +0 -35
- package/dist/packages/crucible/seeds/ts/seed-025 2.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-026 2.ts +0 -36
- package/dist/packages/crucible/seeds/ts/seed-027 2.ts +0 -30
- package/dist/packages/crucible/seeds/ts/seed-028 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-029 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-030 2.ts +0 -37
- package/dist/packages/crucible/seeds/ts/seed-031 2.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-032 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-033 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-034 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-035 2.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-036 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-037 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-038 2.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-039 2.ts +0 -35
- package/dist/packages/crucible/seeds/ts/seed-040 2.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-041 2.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-042 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-043 2.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-044 2.ts +0 -36
- package/dist/packages/crucible/seeds/ts/seed-045 2.ts +0 -39
- package/dist/packages/crucible/seeds/ts/seed-046 2.ts +0 -41
- package/dist/packages/crucible/seeds/ts/seed-047 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-048 2.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-049 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-050 2.ts +0 -41
- package/dist/packages/crucible/seeds/ts/seed-051 2.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-052 2.ts +0 -36
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Command::new with user-controlled string passed to shell
|
|
6
|
-
|
|
7
|
-
use std::process::Command;
|
|
8
|
-
|
|
9
|
-
pub fn ping_host(host: &str) -> String {
|
|
10
|
-
let output = Command::new("sh")
|
|
11
|
-
.arg("-c")
|
|
12
|
-
.arg(format!("ping -c 4 {}", host))
|
|
13
|
-
.output()
|
|
14
|
-
.expect("Failed to execute ping");
|
|
15
|
-
String::from_utf8_lossy(&output.stdout).to_string()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
pub fn lookup_dns(domain: &str) -> String {
|
|
19
|
-
let cmd = format!("nslookup {}", domain);
|
|
20
|
-
let output = Command::new("/bin/sh").arg("-c").arg(cmd).output().unwrap();
|
|
21
|
-
String::from_utf8_lossy(&output.stdout).to_string()
|
|
22
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: User input interpolated into shell command string passed to sh -c
|
|
6
|
-
|
|
7
|
-
use std::process::Command;
|
|
8
|
-
|
|
9
|
-
pub fn convert_image(input: &str, output: &str, width: &str, height: &str) -> bool {
|
|
10
|
-
let cmd = format!("convert {} -resize {}x{} {}", input, width, height, output);
|
|
11
|
-
Command::new("sh")
|
|
12
|
-
.arg("-c")
|
|
13
|
-
.arg(&cmd)
|
|
14
|
-
.status()
|
|
15
|
-
.map(|s| s.success())
|
|
16
|
-
.unwrap_or(false)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn compress_file(path: &str, dest: &str) -> i32 {
|
|
20
|
-
let cmd = format!("gzip -c {} > {}", path, dest);
|
|
21
|
-
Command::new("bash")
|
|
22
|
-
.arg("-c")
|
|
23
|
-
.arg(cmd)
|
|
24
|
-
.status()
|
|
25
|
-
.unwrap()
|
|
26
|
-
.code()
|
|
27
|
-
.unwrap_or(-1)
|
|
28
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Actix-web handler passes query param to shell command
|
|
6
|
-
|
|
7
|
-
use actix_web::{web, HttpResponse};
|
|
8
|
-
use std::process::Command;
|
|
9
|
-
|
|
10
|
-
pub async fn run_report(query: web::Query<std::collections::HashMap<String, String>>) -> HttpResponse {
|
|
11
|
-
let report_type = query.get("type").map(|s| s.as_str()).unwrap_or("summary");
|
|
12
|
-
let date = query.get("date").unwrap_or(&String::new()).clone();
|
|
13
|
-
let cmd = format!("./scripts/generate_report.sh {} {}", report_type, date);
|
|
14
|
-
let output = Command::new("sh").arg("-c").arg(cmd).output().unwrap();
|
|
15
|
-
HttpResponse::Ok().body(String::from_utf8_lossy(&output.stdout).to_string())
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
pub async fn execute_tool(body: String) -> HttpResponse {
|
|
19
|
-
let output = Command::new("sh").arg("-c").arg(&body).output().unwrap();
|
|
20
|
-
HttpResponse::Ok().body(String::from_utf8_lossy(&output.stdout).to_string())
|
|
21
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Git command constructed with user-supplied repository URL
|
|
6
|
-
|
|
7
|
-
use std::process::Command;
|
|
8
|
-
|
|
9
|
-
pub fn clone_repo(url: &str, dest: &str) -> bool {
|
|
10
|
-
let output = Command::new("git")
|
|
11
|
-
.arg("clone")
|
|
12
|
-
.arg(url)
|
|
13
|
-
.arg(dest)
|
|
14
|
-
.output();
|
|
15
|
-
|
|
16
|
-
match output {
|
|
17
|
-
Ok(o) => o.status.success(),
|
|
18
|
-
Err(_) => false,
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
pub fn git_pull(repo_path: &str, branch: &str) -> String {
|
|
23
|
-
let cmd = format!("cd {} && git pull origin {}", repo_path, branch);
|
|
24
|
-
let out = Command::new("sh").arg("-c").arg(cmd).output().unwrap();
|
|
25
|
-
String::from_utf8_lossy(&out.stdout).to_string()
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
pub fn run_git_hook(hook_name: &str, args: &str) -> i32 {
|
|
29
|
-
Command::new("sh")
|
|
30
|
-
.arg("-c")
|
|
31
|
-
.arg(format!(".git/hooks/{} {}", hook_name, args))
|
|
32
|
-
.status()
|
|
33
|
-
.unwrap()
|
|
34
|
-
.code()
|
|
35
|
-
.unwrap_or(-1)
|
|
36
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: ffmpeg command built from user-controlled filenames without sanitization
|
|
6
|
-
|
|
7
|
-
use std::process::Command;
|
|
8
|
-
use std::path::Path;
|
|
9
|
-
|
|
10
|
-
pub fn transcode_video(input: &str, output: &str, codec: &str) -> std::io::Result<()> {
|
|
11
|
-
let cmd = format!("ffmpeg -i '{}' -c:v {} '{}'", input, codec, output);
|
|
12
|
-
Command::new("sh").arg("-c").arg(&cmd).status()?;
|
|
13
|
-
Ok(())
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
pub fn extract_audio(video_path: &str, audio_path: &str) -> bool {
|
|
17
|
-
let ffmpeg_cmd = format!(
|
|
18
|
-
"ffmpeg -i {} -vn -acodec copy {}",
|
|
19
|
-
video_path, audio_path
|
|
20
|
-
);
|
|
21
|
-
Command::new("/bin/sh")
|
|
22
|
-
.arg("-c")
|
|
23
|
-
.arg(ffmpeg_cmd)
|
|
24
|
-
.status()
|
|
25
|
-
.map(|s| s.success())
|
|
26
|
-
.unwrap_or(false)
|
|
27
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: eval-like dynamic command execution from deserialized task payload
|
|
6
|
-
|
|
7
|
-
use std::process::Command;
|
|
8
|
-
use serde::Deserialize;
|
|
9
|
-
|
|
10
|
-
#[derive(Deserialize)]
|
|
11
|
-
pub struct Task {
|
|
12
|
-
pub command: String,
|
|
13
|
-
pub args: Vec<String>,
|
|
14
|
-
pub shell: Option<bool>,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
pub fn execute_task(task: &Task) -> String {
|
|
18
|
-
if task.shell.unwrap_or(false) {
|
|
19
|
-
let full_cmd = format!("{} {}", task.command, task.args.join(" "));
|
|
20
|
-
let out = Command::new("sh").arg("-c").arg(&full_cmd).output().unwrap();
|
|
21
|
-
String::from_utf8_lossy(&out.stdout).to_string()
|
|
22
|
-
} else {
|
|
23
|
-
let out = Command::new(&task.command).args(&task.args).output().unwrap();
|
|
24
|
-
String::from_utf8_lossy(&out.stdout).to_string()
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: command_injection
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Log archival uses user-supplied log name in shell command
|
|
6
|
-
|
|
7
|
-
use std::process::Command;
|
|
8
|
-
|
|
9
|
-
pub fn archive_logs(log_name: &str, destination: &str) -> bool {
|
|
10
|
-
let cmd = format!("tar -czf {}/{}.tar.gz /var/log/{}", destination, log_name, log_name);
|
|
11
|
-
Command::new("sh")
|
|
12
|
-
.arg("-c")
|
|
13
|
-
.arg(&cmd)
|
|
14
|
-
.status()
|
|
15
|
-
.map(|s| s.success())
|
|
16
|
-
.unwrap_or(false)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn rotate_log(service_name: &str) -> String {
|
|
20
|
-
let cmd = format!("logrotate -f /etc/logrotate.d/{}", service_name);
|
|
21
|
-
let output = Command::new("/bin/sh").arg("-c").arg(cmd).output().unwrap();
|
|
22
|
-
String::from_utf8_lossy(&output.stdout).to_string()
|
|
23
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: path_traversal
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: fs::read with user-supplied path — no canonicalization or prefix check
|
|
6
|
-
|
|
7
|
-
use std::fs;
|
|
8
|
-
use std::path::Path;
|
|
9
|
-
|
|
10
|
-
pub fn read_file(base_dir: &str, filename: &str) -> Result<Vec<u8>, std::io::Error> {
|
|
11
|
-
let path = format!("{}/{}", base_dir, filename);
|
|
12
|
-
fs::read(&path)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub fn get_file_content(user_path: &str) -> String {
|
|
16
|
-
let full_path = format!("/var/app/uploads/{}", user_path);
|
|
17
|
-
fs::read_to_string(&full_path).unwrap_or_default()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
pub fn file_exists(base: &str, name: &str) -> bool {
|
|
21
|
-
Path::new(&format!("{}/{}", base, name)).exists()
|
|
22
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: path_traversal
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Axum handler reads file from path parameter without validation
|
|
6
|
-
|
|
7
|
-
use axum::{extract::Path, response::IntoResponse};
|
|
8
|
-
use std::fs;
|
|
9
|
-
|
|
10
|
-
pub async fn serve_file(Path(filename): Path<String>) -> impl IntoResponse {
|
|
11
|
-
let path = format!("/app/static/{}", filename);
|
|
12
|
-
match fs::read_to_string(&path) {
|
|
13
|
-
Ok(content) => (axum::http::StatusCode::OK, content),
|
|
14
|
-
Err(_) => (axum::http::StatusCode::NOT_FOUND, "Not found".to_string()),
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
pub async fn download_resource(Path((dir, file)): Path<(String, String)>) -> impl IntoResponse {
|
|
19
|
-
let full_path = format!("/data/{}/{}", dir, file);
|
|
20
|
-
match fs::read(&full_path) {
|
|
21
|
-
Ok(bytes) => (axum::http::StatusCode::OK, format!("{} bytes", bytes.len())),
|
|
22
|
-
Err(e) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: path_traversal
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Zip extraction writes to path from ZipFile entry without canonicalization
|
|
6
|
-
|
|
7
|
-
use std::fs::{self, File};
|
|
8
|
-
use std::io;
|
|
9
|
-
use std::path::Path;
|
|
10
|
-
use zip::ZipArchive;
|
|
11
|
-
|
|
12
|
-
pub fn extract_zip(zip_path: &str, dest: &str) -> io::Result<()> {
|
|
13
|
-
let file = File::open(zip_path)?;
|
|
14
|
-
let mut archive = ZipArchive::new(file)?;
|
|
15
|
-
for i in 0..archive.len() {
|
|
16
|
-
let mut entry = archive.by_index(i)?;
|
|
17
|
-
let out_path = Path::new(dest).join(entry.name());
|
|
18
|
-
if entry.is_dir() {
|
|
19
|
-
fs::create_dir_all(&out_path)?;
|
|
20
|
-
} else {
|
|
21
|
-
if let Some(parent) = out_path.parent() {
|
|
22
|
-
fs::create_dir_all(parent)?;
|
|
23
|
-
}
|
|
24
|
-
let mut outfile = File::create(&out_path)?;
|
|
25
|
-
io::copy(&mut entry, &mut outfile)?;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
Ok(())
|
|
29
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: path_traversal
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: File write to user-controlled path without restricting to base directory
|
|
6
|
-
|
|
7
|
-
use std::fs;
|
|
8
|
-
|
|
9
|
-
pub fn write_report(report_name: &str, content: &str) -> Result<(), std::io::Error> {
|
|
10
|
-
let path = format!("/var/reports/{}", report_name);
|
|
11
|
-
fs::write(&path, content)?;
|
|
12
|
-
Ok(())
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub fn save_template(template_id: &str, subdir: &str, data: &[u8]) -> bool {
|
|
16
|
-
let full_path = format!("/app/templates/{}/{}.tmpl", subdir, template_id);
|
|
17
|
-
fs::write(&full_path, data).is_ok()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
pub fn cache_response(cache_key: &str, data: &str) -> bool {
|
|
21
|
-
let path = format!("/tmp/cache/{}", cache_key);
|
|
22
|
-
fs::write(path, data).is_ok()
|
|
23
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: path_traversal
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Config file loaded from user-controlled path — arbitrary file read
|
|
6
|
-
|
|
7
|
-
use std::fs;
|
|
8
|
-
use serde_json::Value;
|
|
9
|
-
|
|
10
|
-
pub fn load_config(config_name: &str) -> Value {
|
|
11
|
-
let path = format!("/etc/app/configs/{}", config_name);
|
|
12
|
-
let content = fs::read_to_string(&path).unwrap_or_else(|_| "{}".to_string());
|
|
13
|
-
serde_json::from_str(&content).unwrap_or(Value::Null)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
pub fn load_plugin_manifest(plugin_id: &str) -> String {
|
|
17
|
-
let manifest_path = format!("./plugins/{}/manifest.json", plugin_id);
|
|
18
|
-
fs::read_to_string(manifest_path).unwrap_or_default()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
pub fn read_user_template(user_id: &str, template_name: &str) -> String {
|
|
22
|
-
let path = format!("/data/users/{}/templates/{}", user_id, template_name);
|
|
23
|
-
fs::read_to_string(path).unwrap_or_default()
|
|
24
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: path_traversal
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Directory listing served from user-controlled subdirectory
|
|
6
|
-
|
|
7
|
-
use std::fs;
|
|
8
|
-
|
|
9
|
-
pub fn list_directory(base: &str, subdir: &str) -> Vec<String> {
|
|
10
|
-
let path = format!("{}/{}", base, subdir);
|
|
11
|
-
match fs::read_dir(&path) {
|
|
12
|
-
Ok(entries) => entries
|
|
13
|
-
.filter_map(|e| e.ok())
|
|
14
|
-
.map(|e| e.file_name().to_string_lossy().to_string())
|
|
15
|
-
.collect(),
|
|
16
|
-
Err(_) => vec![],
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
pub fn serve_directory(root: &str, user_path: &str) -> String {
|
|
21
|
-
let full = format!("{}/{}", root, user_path);
|
|
22
|
-
fs::read_dir(&full)
|
|
23
|
-
.map(|rd| rd.count().to_string())
|
|
24
|
-
.unwrap_or_else(|e| e.to_string())
|
|
25
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: integer_overflow
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Explicit unchecked arithmetic that wraps on overflow in release mode
|
|
6
|
-
|
|
7
|
-
pub fn calculate_offset(index: usize, stride: usize) -> usize {
|
|
8
|
-
index * stride
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
pub fn buffer_size(width: u32, height: u32, channels: u32) -> u32 {
|
|
12
|
-
width * height * channels
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub fn total_price(unit_price: u32, quantity: u32) -> u32 {
|
|
16
|
-
unit_price * quantity
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn encode_coords(x: i32, y: i32) -> i64 {
|
|
20
|
-
(x as i64) * 100000 + (y as i64)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pub fn as_usize_unchecked(n: i64) -> usize {
|
|
24
|
-
n as usize
|
|
25
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: integer_overflow
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Cast truncation — u64 to u32/u16/u8 silently drops high bits
|
|
6
|
-
|
|
7
|
-
pub fn truncate_to_u8(val: u64) -> u8 {
|
|
8
|
-
val as u8
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
pub fn truncate_to_u16(val: u64) -> u16 {
|
|
12
|
-
val as u16
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub fn index_from_user_input(input: i64) -> usize {
|
|
16
|
-
input as usize
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn packet_len_from_i32(len: i32) -> usize {
|
|
20
|
-
len as usize
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pub fn compress_timestamp(ts: u64) -> u32 {
|
|
24
|
-
ts as u32
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
pub fn negate_unsigned(n: u32) -> u32 {
|
|
28
|
-
let signed = n as i32;
|
|
29
|
-
(-signed) as u32
|
|
30
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: integer_overflow
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Allocation size computed from untrusted user input — potential OOM / overflow
|
|
6
|
-
|
|
7
|
-
use std::alloc::{alloc, Layout};
|
|
8
|
-
|
|
9
|
-
pub fn allocate_buffer(user_size: u32, element_size: u32) -> Vec<u8> {
|
|
10
|
-
let total = (user_size * element_size) as usize;
|
|
11
|
-
vec![0u8; total]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
pub unsafe fn raw_alloc(count: usize, item_size: usize) -> *mut u8 {
|
|
15
|
-
let size = count * item_size;
|
|
16
|
-
let layout = Layout::from_size_align_unchecked(size, 8);
|
|
17
|
-
alloc(layout)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
pub fn string_repeat(s: &str, n: u32) -> String {
|
|
21
|
-
s.repeat(n as usize)
|
|
22
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: integer_overflow
|
|
3
|
-
// severity: medium
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Signed integer arithmetic without overflow check — negative length risk
|
|
6
|
-
|
|
7
|
-
pub fn calculate_remaining(total: i32, used: i32) -> i32 {
|
|
8
|
-
total - used
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
pub fn chunk_count(data_len: i32, chunk_size: i32) -> i32 {
|
|
12
|
-
(data_len + chunk_size - 1) / chunk_size
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub fn ring_buffer_advance(pos: u32, step: u32, capacity: u32) -> u32 {
|
|
16
|
-
(pos + step) % capacity
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn decode_delta(base: u32, delta: i32) -> u32 {
|
|
20
|
-
(base as i32 + delta) as u32
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pub fn safe_subtract(a: u32, b: u32) -> u32 {
|
|
24
|
-
a - b
|
|
25
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: integer_overflow
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Array index from user input without bounds check — potential panic or OOM
|
|
6
|
-
|
|
7
|
-
pub fn get_element(data: &[u8], index: usize) -> u8 {
|
|
8
|
-
data[index]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
pub fn set_element(data: &mut Vec<u8>, index: usize, value: u8) {
|
|
12
|
-
data[index] = value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub fn read_matrix(matrix: &[Vec<i32>], row: usize, col: usize) -> i32 {
|
|
16
|
-
matrix[row][col]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
pub fn sum_range(data: &[i64], start: usize, end: usize) -> i64 {
|
|
20
|
-
data[start..end].iter().sum()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pub fn nth_element(data: &[String], n: usize) -> &str {
|
|
24
|
-
&data[n]
|
|
25
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: ssrf
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: reqwest::get called with user-controlled URL — SSRF
|
|
6
|
-
|
|
7
|
-
use reqwest;
|
|
8
|
-
|
|
9
|
-
pub async fn fetch_url(user_url: &str) -> Result<String, reqwest::Error> {
|
|
10
|
-
let response = reqwest::get(user_url).await?;
|
|
11
|
-
let body = response.text().await?;
|
|
12
|
-
Ok(body)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
pub async fn proxy_request(target: &str) -> String {
|
|
16
|
-
reqwest::get(target)
|
|
17
|
-
.await
|
|
18
|
-
.and_then(|r| async { r.text().await }.await.map_err(Into::into))
|
|
19
|
-
.unwrap_or_default()
|
|
20
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: ssrf
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Axum endpoint fetches user-supplied URL server-side — open proxy / SSRF
|
|
6
|
-
|
|
7
|
-
use axum::{extract::Query, response::IntoResponse};
|
|
8
|
-
use reqwest::Client;
|
|
9
|
-
use std::collections::HashMap;
|
|
10
|
-
|
|
11
|
-
pub async fn proxy_endpoint(Query(params): Query<HashMap<String, String>>) -> impl IntoResponse {
|
|
12
|
-
let url = params.get("url").cloned().unwrap_or_default();
|
|
13
|
-
let client = Client::new();
|
|
14
|
-
match client.get(&url).send().await {
|
|
15
|
-
Ok(resp) => resp.text().await.unwrap_or_default(),
|
|
16
|
-
Err(e) => format!("Error: {}", e),
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
pub async fn webhook_relay(Query(params): Query<HashMap<String, String>>) -> impl IntoResponse {
|
|
21
|
-
let callback = params.get("callback").cloned().unwrap_or_default();
|
|
22
|
-
let _ = Client::new()
|
|
23
|
-
.post(&callback)
|
|
24
|
-
.json(&serde_json::json!({"status": "ok"}))
|
|
25
|
-
.send()
|
|
26
|
-
.await;
|
|
27
|
-
"Triggered"
|
|
28
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: ssrf
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: URL constructed from user-supplied hostname and path — SSRF
|
|
6
|
-
|
|
7
|
-
use reqwest::Client;
|
|
8
|
-
|
|
9
|
-
pub async fn check_endpoint(host: &str, port: u16, path: &str) -> bool {
|
|
10
|
-
let url = format!("http://{}:{}{}", host, port, path);
|
|
11
|
-
Client::new().get(&url).send().await.is_ok()
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
pub async fn fetch_avatar(base_url: &str, user_id: &str) -> Vec<u8> {
|
|
15
|
-
let url = format!("{}/avatars/{}.png", base_url, user_id);
|
|
16
|
-
reqwest::get(&url)
|
|
17
|
-
.await
|
|
18
|
-
.and_then(|r| async { r.bytes().await }.await.map_err(Into::into))
|
|
19
|
-
.map(|b| b.to_vec())
|
|
20
|
-
.unwrap_or_default()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pub async fn import_data(source_url: &str) -> serde_json::Value {
|
|
24
|
-
let text = reqwest::get(source_url).await.unwrap().text().await.unwrap();
|
|
25
|
-
serde_json::from_str(&text).unwrap_or(serde_json::Value::Null)
|
|
26
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: ssrf
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Image URL fetched server-side from user POST body — SSRF via metadata endpoint
|
|
6
|
-
|
|
7
|
-
use reqwest::Client;
|
|
8
|
-
use serde::Deserialize;
|
|
9
|
-
|
|
10
|
-
#[derive(Deserialize)]
|
|
11
|
-
pub struct ImageRequest {
|
|
12
|
-
pub url: String,
|
|
13
|
-
pub format: String,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
pub async fn download_and_process(req: ImageRequest) -> Vec<u8> {
|
|
17
|
-
let client = Client::new();
|
|
18
|
-
let bytes = client
|
|
19
|
-
.get(&req.url)
|
|
20
|
-
.send()
|
|
21
|
-
.await
|
|
22
|
-
.unwrap()
|
|
23
|
-
.bytes()
|
|
24
|
-
.await
|
|
25
|
-
.unwrap();
|
|
26
|
-
bytes.to_vec()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
pub async fn get_og_metadata(page_url: &str) -> String {
|
|
30
|
-
reqwest::get(page_url).await.unwrap().text().await.unwrap()
|
|
31
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: ssrf
|
|
3
|
-
// severity: critical
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: DNS rebinding-vulnerable health check using user-supplied host
|
|
6
|
-
|
|
7
|
-
use reqwest::Client;
|
|
8
|
-
use std::time::Duration;
|
|
9
|
-
|
|
10
|
-
pub async fn health_check(host: &str) -> serde_json::Value {
|
|
11
|
-
let url = format!("http://{}/health", host);
|
|
12
|
-
let client = Client::builder()
|
|
13
|
-
.timeout(Duration::from_secs(5))
|
|
14
|
-
.build()
|
|
15
|
-
.unwrap();
|
|
16
|
-
match client.get(&url).send().await {
|
|
17
|
-
Ok(resp) => resp.json().await.unwrap_or(serde_json::Value::Null),
|
|
18
|
-
Err(_) => serde_json::json!({"status": "unreachable"}),
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
pub async fn resolve_and_fetch(domain: &str, endpoint: &str) -> String {
|
|
23
|
-
let url = format!("https://{}{}", domain, endpoint);
|
|
24
|
-
reqwest::get(&url).await.unwrap().text().await.unwrap()
|
|
25
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: race_condition
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Arc<Mutex<>> lock held across await point — potential deadlock
|
|
6
|
-
|
|
7
|
-
use std::sync::{Arc, Mutex};
|
|
8
|
-
use tokio::time::{sleep, Duration};
|
|
9
|
-
|
|
10
|
-
pub struct SharedState {
|
|
11
|
-
data: Arc<Mutex<Vec<String>>>,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
impl SharedState {
|
|
15
|
-
pub async fn add_and_process(&self, item: String) {
|
|
16
|
-
let mut guard = self.data.lock().unwrap();
|
|
17
|
-
guard.push(item);
|
|
18
|
-
sleep(Duration::from_millis(100)).await;
|
|
19
|
-
println!("Processed: {:?}", guard.last());
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
pub async fn read_then_write(&self, key: String) -> Option<String> {
|
|
23
|
-
let guard = self.data.lock().unwrap();
|
|
24
|
-
let found = guard.iter().find(|s| s.starts_with(&key)).cloned();
|
|
25
|
-
sleep(Duration::from_secs(1)).await;
|
|
26
|
-
found
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// [CRUCIBLE-SEED]
|
|
2
|
-
// category: race_condition
|
|
3
|
-
// severity: high
|
|
4
|
-
// language: rust
|
|
5
|
-
// expected: Check-then-act race condition on shared file resource
|
|
6
|
-
|
|
7
|
-
use std::fs;
|
|
8
|
-
use std::path::Path;
|
|
9
|
-
|
|
10
|
-
pub fn create_if_not_exists(path: &str, content: &str) -> bool {
|
|
11
|
-
if !Path::new(path).exists() {
|
|
12
|
-
fs::write(path, content).is_ok()
|
|
13
|
-
} else {
|
|
14
|
-
false
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
pub fn atomic_counter_increment(path: &str) -> u64 {
|
|
19
|
-
let current: u64 = fs::read_to_string(path)
|
|
20
|
-
.unwrap_or("0".to_string())
|
|
21
|
-
.trim()
|
|
22
|
-
.parse()
|
|
23
|
-
.unwrap_or(0);
|
|
24
|
-
let next = current + 1;
|
|
25
|
-
fs::write(path, next.to_string()).unwrap();
|
|
26
|
-
next
|
|
27
|
-
}
|