Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

125 lines
5.2 KiB
Go

package astquery
// Rust SAST starter pack. The legacy unsafe-rust-* family already
// covers unsafe blocks / unwrap / panic-macro / assert-macro. This
// file adds security-specific patterns absent from that bundle.
func init() {
registerRustCommandShell()
registerRustWeakCryptoCrate()
registerRustHttpInsecure()
registerRustMemForget()
registerRustTransmute()
registerRustEnvFromUser()
registerRustStdRandForCrypto()
}
func registerRustCommandShell() {
mustRegisterSAST(sastRule{
Name: "rust-command-sh-c",
Description: "`Command::new(\"sh\").arg(\"-c\").arg(cmd)` — runs `cmd` through the shell. Any caller-controlled portion is command injection. Build an argv directly.",
Severity: "error",
CWE: "CWE-78",
OWASP: "A03:2021-Injection",
Tags: []string{"command-injection"},
Pat: map[string]string{
"rust": `((call_expression
function: (scoped_identifier path: (identifier) @ty name: (identifier) @method)
arguments: (arguments (string_literal) @shell)) @match
(#eq? @ty "Command") (#eq? @method "new")
(#match? @shell "\"(sh|bash|zsh|cmd|powershell)\""))`,
},
})
}
func registerRustWeakCryptoCrate() {
mustRegisterSAST(sastRule{
Name: "rust-md5-or-sha1-crate",
Description: "`use md5::*` / `use sha1::*` — crates that wrap broken hashes. Switch to `sha2`, `sha3`, `blake3`, or `argon2`.",
Severity: "error",
CWE: "CWE-327",
Tags: []string{"crypto", "weak-hash"},
Pat: map[string]string{
"rust": `((use_declaration argument: (scoped_use_list path: (identifier) @crate)) @match
(#match? @crate "^(md5|sha1|md4|md2)$"))
((use_declaration argument: (scoped_identifier path: (identifier) @crate)) @match
(#match? @crate "^(md5|sha1|md4|md2)$"))`,
},
})
}
func registerRustHttpInsecure() {
mustRegisterSAST(sastRule{
Name: "rust-reqwest-danger-accept-invalid-certs",
Description: "`reqwest::Client::builder().danger_accept_invalid_certs(true)` — disables TLS validation. Any MITM intercepts.",
Severity: "error",
CWE: "CWE-295",
Tags: []string{"tls", "no-verify"},
Pat: map[string]string{
"rust": `((call_expression
function: (field_expression field: (field_identifier) @fn)
arguments: (arguments (boolean_literal) @v)) @match
(#match? @fn "^(danger_accept_invalid_certs|danger_accept_invalid_hostnames)$") (#eq? @v "true"))`,
},
})
}
func registerRustMemForget() {
mustRegisterSAST(sastRule{
Name: "rust-mem-forget",
Description: "`mem::forget(x)` — leaks `x` without running its destructor. Sometimes intentional (FFI hand-off); usually a bug. Audit every call site.",
Severity: "info",
CWE: "CWE-401",
Tags: []string{"memory", "audit-hook"},
Pat: map[string]string{
"rust": `((call_expression
function: (scoped_identifier path: (identifier) @mod name: (identifier) @fn)) @match
(#eq? @mod "mem") (#eq? @fn "forget"))`,
},
})
}
func registerRustTransmute() {
mustRegisterSAST(sastRule{
Name: "rust-mem-transmute",
Description: "`mem::transmute::<_, _>(...)` — bit-cast between unrelated types. Often the wrong primitive; usually a sign that a `From` / safe API exists. Hand-audit boundary.",
Severity: "warning",
CWE: "CWE-704",
Tags: []string{"memory", "unsafe"},
Pat: map[string]string{
"rust": `((call_expression
function: [(scoped_identifier name: (identifier) @fn) (generic_function function: (scoped_identifier name: (identifier) @fn))]) @match
(#eq? @fn "transmute"))`,
},
})
}
func registerRustEnvFromUser() {
mustRegisterSAST(sastRule{
Name: "rust-env-set-from-user",
Description: "`std::env::set_var(k, v)` — process-wide; thread-unsafe pre-1.0 / racy on Unix. Avoid using it to plumb user input into the env (subsequent processes inherit).",
Severity: "info",
CWE: "CWE-454",
Tags: []string{"env"},
Pat: map[string]string{
"rust": `((call_expression
function: (scoped_identifier path: (scoped_identifier path: (identifier) @std name: (identifier) @env) name: (identifier) @fn)) @match
(#eq? @std "std") (#eq? @env "env") (#eq? @fn "set_var"))`,
},
})
}
func registerRustStdRandForCrypto() {
mustRegisterSAST(sastRule{
Name: "rust-thread-rng-for-crypto",
Description: "`rand::thread_rng()` — chacha20-based and cryptographically secure *in current versions*, but `SmallRng::seed_from_u64` and other `rand` PRNGs are not. Audit each call site to confirm the chosen RNG meets the use case (use `rand::rngs::OsRng` / `ring::rand` for keys / nonces / tokens unambiguously).",
Severity: "info",
CWE: "CWE-338",
Tags: []string{"crypto", "audit-hook"},
Pat: map[string]string{
"rust": `((call_expression function: (scoped_identifier path: (identifier) @crate name: (identifier) @fn)) @match
(#eq? @crate "rand") (#match? @fn "^(thread_rng|random)$"))`,
},
})
}