rust by martinholovsky/claude-skills-generator
npx skills add https://github.com/martinholovsky/claude-skills-generator --skill rust| 关卡 | 状态 | 备注 |
|---|---|---|
| 0.1 领域专业知识 | 通过 | 所有权/借用、unsafe、FFI、async、Tauri 命令 |
| 0.2 漏洞研究 | 通过 | 记录了 3 个以上 CVE(2025-11-20) |
| 0.5 幻觉检查 | 通过 | 示例已针对 rustc 1.75+ 测试 |
| 0.11 文件组织 | 拆分 | 中等风险,主文件约 400 行 + 参考资料 |
风险等级 : 中等
理由 : Rust 通过借用检查器提供内存安全,但 unsafe 代码块、FFI 边界以及通过 std::process::Command 进行的命令注入会带来安全风险。
您是一位专业的 Rust 系统程序员,专注于 Tauri 桌面应用程序开发。您遵循 Rust 惯用法编写内存安全、高性能的代码,同时理解安全代码与不安全代码之间的安全边界。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 场景 | 方法 |
|---|---|
| 共享所有权 | Arc<T>(线程安全)或 Rc<T>(单线程) |
| 内部可变性 | Mutex<T>、RwLock<T> 或 RefCell<T> |
| 性能关键 | 先分析性能,再考虑不安全优化 |
| FFI 交互 | 创建带有验证的安全包装类型 |
| 错误处理 | 返回带有自定义错误类型的 Result<T, E> |
| 类别 | 版本 | 备注 |
|---|---|---|
| LTS/稳定版 | Rust 1.75+ | Tauri 2.x 的最低要求 |
| 推荐版本 | Rust 1.82+ | 带有安全补丁的最新稳定版 |
| Tauri | 2.0+ | 新项目使用 2.x |
| Tokio | 1.35+ | 异步运行时 |
[dependencies]
serde = { version = "1.0", features = ["derive"] }
validator = { version = "0.16", features = ["derive"] }
ring = "0.17" # 加密
argon2 = "0.5" # 密码哈希
dunce = "1.0" # 安全路径规范化
[dev-dependencies]
cargo-audit = "0.18" # 漏洞扫描
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_creation_valid_input() {
let input = UserInput { name: "Alice".to_string(), age: 30 };
let result = User::try_from(input);
assert!(result.is_ok());
assert_eq!(result.unwrap().name, "Alice");
}
#[test]
fn test_user_creation_rejects_empty_name() {
let input = UserInput { name: "".to_string(), age: 25 };
assert!(matches!(User::try_from(input), Err(AppError::Validation(_))));
}
#[tokio::test]
async fn test_async_state_concurrent_access() {
let state = AppState::new();
let state_clone = state.clone();
let handle = tokio::spawn(async move {
state_clone.update_user("1", User::new("Bob")).await
});
state.update_user("2", User::new("Alice")).await.unwrap();
handle.await.unwrap().unwrap();
assert!(state.get_user("1").await.is_some());
}
}
impl TryFrom<UserInput> for User {
type Error = AppError;
fn try_from(input: UserInput) -> Result<Self, Self::Error> {
if input.name.is_empty() {
return Err(AppError::Validation("Name cannot be empty".into()));
}
Ok(User { name: input.name, age: input.age })
}
}
cargo test && cargo clippy -- -D warnings && cargo audit
使用 validator crate 和自定义正则表达式模式验证所有 Tauri 命令输入。
use serde::Deserialize;
use validator::Validate;
#[derive(Deserialize, Validate)]
pub struct UserInput {
#[validate(length(min = 1, max = 100), regex(path = "SAFE_STRING_REGEX"))]
pub name: String,
#[validate(range(min = 0, max = 120))]
pub age: u8,
}
#[tauri::command]
pub async fn create_user(input: UserInput) -> Result<User, String> {
input.validate().map_err(|e| format!("Validation error: {}", e))?;
Ok(User::new(input))
}
有关包含正则表达式定义的完整验证模式,请参阅
references/advanced-patterns.md
使用 thiserror 创建结构化错误,这些错误可以安全序列化而不暴露内部信息。
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(#[from] sqlx::Error),
#[error("Validation failed: {0}")]
Validation(String),
#[error("Not found")]
NotFound,
}
impl serde::Serialize for AppError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
serializer.serialize_str(&self.to_string()) // 永不暴露内部信息
}
}
通过规范化路径和验证包含关系来防止路径遍历。
pub fn safe_path_join(base: &Path, user_input: &str) -> Result<PathBuf, AppError> {
if user_input.contains("..") || user_input.contains("~") {
return Err(AppError::Validation("Invalid path characters".into()));
}
let canonical = dunce::canonicalize(base.join(user_input))
.map_err(|_| AppError::NotFound)?;
let base_canonical = dunce::canonicalize(base)
.map_err(|_| AppError::Internal(anyhow::anyhow!("Invalid base")))?;
if !canonical.starts_with(&base_canonical) {
return Err(AppError::Validation("Path traversal detected".into()));
}
Ok(canonical)
}
通过使用白名单和避免 shell 执行来缓解 CVE-2024-24576。
pub fn safe_command(program: &str, args: &[&str]) -> Result<String, AppError> {
const ALLOWED: &[&str] = &["git", "cargo", "rustc"];
if !ALLOWED.contains(&program) {
return Err(AppError::Validation("Program not allowed".into()));
}
let output = Command::new(program).args(args).output()
.map_err(|e| AppError::Internal(e.into()))?;
if output.status.success() {
String::from_utf8(output.stdout).map_err(|e| AppError::Internal(e.into()))
} else {
Err(AppError::Internal(anyhow::anyhow!("Command failed")))
}
}
在 Tauri 应用程序中使用 Arc<RwLock> 实现线程安全的共享状态。
pub struct AppState {
users: Arc<RwLock<HashMap<String, User>>>,
config: Arc<Config>,
}
impl AppState {
pub async fn get_user(&self, id: &str) -> Option<User> {
self.users.read().await.get(id).cloned()
}
pub async fn update_user(&self, id: &str, user: User) -> Result<(), AppError> {
self.users.write().await.insert(id.to_string(), user);
Ok(())
}
}
有关高级状态模式和 Tauri 集成,请参阅
references/advanced-patterns.md
| CVE ID | 严重性 | 描述 | 缓解措施 |
|---|---|---|---|
| CVE-2024-24576 | 严重 | 通过批处理文件进行命令注入(Windows) | Rust 1.77.2+,避免使用 shell |
| CVE-2024-43402 | 高 | 对上述问题的修复不完整 | Rust 1.81.0+ |
| CVE-2021-28032 | 高 | 不安全代码块中存在多个可变引用 | 审计不安全代码块 |
有关完整的 CVE 详情和缓解代码,请参阅
references/security-examples.md
| 类别 | 风险 | 关键缓解措施 |
|---|---|---|
| A01 访问控制失效 | 中等 | 在 Tauri 命令中验证权限 |
| A03 注入 | 高 | 不使用 shell 的命令、参数化查询 |
| A04 不安全设计 | 中等 | 使用类型系统强制执行不变量 |
| A06 易受攻击的组件 | 高 | 定期运行 cargo-audit |
四层方法 : 类型系统新类型 -> 模式验证(serde/validator)-> 业务逻辑 -> 输出编码
pub struct Email(String); // 用于已验证输入的新类型
impl Email {
pub fn new(s: &str) -> Result<Self, ValidationError> {
if validator::validate_email(s) { Ok(Self(s.to_string())) }
else { Err(ValidationError::InvalidEmail) }
}
}
// 从环境变量或带有加密的 tauri-plugin-store 加载
fn get_api_key() -> Result<String, AppError> {
std::env::var("API_KEY")
.map_err(|_| AppError::Configuration("API_KEY not set".into()))
}
有关安全存储模式,请参阅
references/security-examples.md
差 : data.to_vec() 然后迭代 - 好 : 返回带生命周期的迭代器
// 差: fn process(data: &[u8]) -> Vec<u8> { data.to_vec().iter().map(|b| b+1).collect() }
fn process(data: &[u8]) -> impl Iterator<Item = u8> + '_ {
data.iter().map(|b| b + 1) // 无分配
}
差 : 手动循环并 push - 好 : 迭代器链(惰性、融合)
fn filter_transform(items: &[Item]) -> Vec<String> {
items.iter().filter(|i| i.is_valid()).map(|i| i.name.to_uppercase()).collect()
}
差 : 在热点路径中使用 Vec::with_capacity() - 好 : 对象池
static BUFFER_POOL: Lazy<Pool<Vec<u8>>> = Lazy::new(|| Pool::new(32, || Vec::with_capacity(1024)));
async fn handle_request(data: &[u8]) -> Vec<u8> {
let mut buffer = BUFFER_POOL.pull(|| Vec::with_capacity(1024));
buffer.clear(); process(&mut buffer, data); buffer.to_vec()
}
差 : 在异步中执行 CPU 密集型工作 - 好 : 对 CPU 密集型任务使用 spawn_blocking
async fn hash_password(password: String) -> Result<String, AppError> {
tokio::task::spawn_blocking(move || {
argon2::hash_encoded(password.as_bytes(), &salt, &config)
.map_err(|e| AppError::Internal(e.into()))
}).await?
}
差 : println! 会分配 - 好 : 使用 write! 写入预分配缓冲区
fn log_metric(buffer: &mut Vec<u8>, name: &str, value: u64) {
buffer.clear();
write!(buffer, "{}: {}", name, value).unwrap();
std::io::stdout().write_all(buffer).unwrap();
}
cargo audit # 依赖项漏洞
cargo +nightly careful test # 内存安全检查
cargo clippy -- -D warnings # 包含安全警告的代码检查
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_traversal_blocked() {
let base = Path::new("/app/data");
assert!(safe_path_join(base, "../etc/passwd").is_err());
assert!(safe_path_join(base, "user/file.txt").is_ok());
}
#[test]
fn test_command_allowlist() {
assert!(safe_command("rm", &["-rf", "/"]).is_err());
assert!(safe_command("git", &["status"]).is_ok());
}
}
有关模糊测试和集成测试模式,请参阅
references/advanced-patterns.md
| 反模式 | 问题 | 解决方案 |
|---|---|---|
在生产环境中使用 .unwrap() | Panic 导致应用崩溃 | 使用 ? 配合 Result |
| 没有文档的 Unsafe | 未经验证的不变量 | 添加 // SAFETY: 注释 |
| Shell 命令执行 | 注入漏洞 | 直接使用 Command::new() |
| 忽略 Clippy | 错过安全警告 | 运行 cargo clippy -- -D warnings |
| 硬编码凭据 | 代码中的密钥 | 使用环境变量或安全存储 |
// 绝对不要:Shell 注入
Command::new("sh").arg("-c").arg(format!("echo {}", user_input));
// 始终:直接执行
Command::new("echo").arg(user_input);
cargo audit 检查依赖项// SAFETY: 注释记录所有不安全代码块cargo test - 所有测试通过cargo clippy -- -D warnings - 无警告cargo audit - 零个高/严重漏洞您的目标是创建具有以下特性的 Rust 代码:
关键安全提醒 :
有关详细示例和高级模式,请参阅
references/目录
每周安装量
102
代码仓库
GitHub 星标数
32
首次出现
2026年1月20日
安全审计
安装于
opencode81
gemini-cli81
codex81
github-copilot78
cursor75
claude-code69
| Gate | Status | Notes |
|---|---|---|
| 0.1 Domain Expertise | PASSED | Ownership/borrowing, unsafe, FFI, async, Tauri commands |
| 0.2 Vulnerability Research | PASSED | 3+ CVEs documented (2025-11-20) |
| 0.5 Hallucination Check | PASSED | Examples tested against rustc 1.75+ |
| 0.11 File Organization | Split | MEDIUM-RISK, ~400 lines main + references |
Risk Level : MEDIUM
Justification : Rust provides memory safety through the borrow checker, but unsafe blocks, FFI boundaries, and command injection via std::process::Command present security risks.
You are an expert Rust systems programmer specializing in Tauri desktop application development. You write memory-safe, performant code following Rust idioms while understanding security boundaries between safe and unsafe code.
| Situation | Approach |
|---|---|
| Shared ownership | Arc<T> (thread-safe) or Rc<T> (single-thread) |
| Interior mutability | Mutex<T>, RwLock<T>, or RefCell<T> |
| Performance-critical | Profile first, then consider unsafe optimizations |
| FFI interaction | Create safe wrapper types with validation |
| Error handling | Return Result<T, E> with custom error types |
| Category | Version | Notes |
|---|---|---|
| LTS/Stable | Rust 1.75+ | Minimum for Tauri 2.x |
| Recommended | Rust 1.82+ | Latest stable with security patches |
| Tauri | 2.0+ | Use 2.x for new projects |
| Tokio | 1.35+ | Async runtime |
[dependencies]
serde = { version = "1.0", features = ["derive"] }
validator = { version = "0.16", features = ["derive"] }
ring = "0.17" # Cryptography
argon2 = "0.5" # Password hashing
dunce = "1.0" # Safe path canonicalization
[dev-dependencies]
cargo-audit = "0.18" # Vulnerability scanning
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_creation_valid_input() {
let input = UserInput { name: "Alice".to_string(), age: 30 };
let result = User::try_from(input);
assert!(result.is_ok());
assert_eq!(result.unwrap().name, "Alice");
}
#[test]
fn test_user_creation_rejects_empty_name() {
let input = UserInput { name: "".to_string(), age: 25 };
assert!(matches!(User::try_from(input), Err(AppError::Validation(_))));
}
#[tokio::test]
async fn test_async_state_concurrent_access() {
let state = AppState::new();
let state_clone = state.clone();
let handle = tokio::spawn(async move {
state_clone.update_user("1", User::new("Bob")).await
});
state.update_user("2", User::new("Alice")).await.unwrap();
handle.await.unwrap().unwrap();
assert!(state.get_user("1").await.is_some());
}
}
impl TryFrom<UserInput> for User {
type Error = AppError;
fn try_from(input: UserInput) -> Result<Self, Self::Error> {
if input.name.is_empty() {
return Err(AppError::Validation("Name cannot be empty".into()));
}
Ok(User { name: input.name, age: input.age })
}
}
cargo test && cargo clippy -- -D warnings && cargo audit
Validate all Tauri command inputs using the validator crate with custom regex patterns.
use serde::Deserialize;
use validator::Validate;
#[derive(Deserialize, Validate)]
pub struct UserInput {
#[validate(length(min = 1, max = 100), regex(path = "SAFE_STRING_REGEX"))]
pub name: String,
#[validate(range(min = 0, max = 120))]
pub age: u8,
}
#[tauri::command]
pub async fn create_user(input: UserInput) -> Result<User, String> {
input.validate().map_err(|e| format!("Validation error: {}", e))?;
Ok(User::new(input))
}
See
references/advanced-patterns.mdfor complete validation patterns with regex definitions
Use thiserror for structured errors that serialize safely without exposing internals.
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(#[from] sqlx::Error),
#[error("Validation failed: {0}")]
Validation(String),
#[error("Not found")]
NotFound,
}
impl serde::Serialize for AppError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
serializer.serialize_str(&self.to_string()) // Never expose internals
}
}
Prevent path traversal by canonicalizing paths and verifying containment.
pub fn safe_path_join(base: &Path, user_input: &str) -> Result<PathBuf, AppError> {
if user_input.contains("..") || user_input.contains("~") {
return Err(AppError::Validation("Invalid path characters".into()));
}
let canonical = dunce::canonicalize(base.join(user_input))
.map_err(|_| AppError::NotFound)?;
let base_canonical = dunce::canonicalize(base)
.map_err(|_| AppError::Internal(anyhow::anyhow!("Invalid base")))?;
if !canonical.starts_with(&base_canonical) {
return Err(AppError::Validation("Path traversal detected".into()));
}
Ok(canonical)
}
Mitigate CVE-2024-24576 by using allowlists and avoiding shell execution.
pub fn safe_command(program: &str, args: &[&str]) -> Result<String, AppError> {
const ALLOWED: &[&str] = &["git", "cargo", "rustc"];
if !ALLOWED.contains(&program) {
return Err(AppError::Validation("Program not allowed".into()));
}
let output = Command::new(program).args(args).output()
.map_err(|e| AppError::Internal(e.into()))?;
if output.status.success() {
String::from_utf8(output.stdout).map_err(|e| AppError::Internal(e.into()))
} else {
Err(AppError::Internal(anyhow::anyhow!("Command failed")))
}
}
Use Arc<RwLock> for thread-safe shared state in Tauri applications.
pub struct AppState {
users: Arc<RwLock<HashMap<String, User>>>,
config: Arc<Config>,
}
impl AppState {
pub async fn get_user(&self, id: &str) -> Option<User> {
self.users.read().await.get(id).cloned()
}
pub async fn update_user(&self, id: &str, user: User) -> Result<(), AppError> {
self.users.write().await.insert(id.to_string(), user);
Ok(())
}
}
See
references/advanced-patterns.mdfor advanced state patterns and Tauri integration
| CVE ID | Severity | Description | Mitigation |
|---|---|---|---|
| CVE-2024-24576 | CRITICAL | Command injection via batch files (Windows) | Rust 1.77.2+, avoid shell |
| CVE-2024-43402 | HIGH | Incomplete fix for above | Rust 1.81.0+ |
| CVE-2021-28032 | HIGH | Multiple mutable references in unsafe | Audit unsafe blocks |
See
references/security-examples.mdfor complete CVE details and mitigation code
| Category | Risk | Key Mitigations |
|---|---|---|
| A01 Broken Access Control | MEDIUM | Validate permissions in Tauri commands |
| A03 Injection | HIGH | Command without shell, parameterized queries |
| A04 Insecure Design | MEDIUM | Type system to enforce invariants |
| A06 Vulnerable Components | HIGH | Run cargo-audit regularly |
Four-layer approach : Type system newtypes -> Schema validation (serde/validator) -> Business logic -> Output encoding
pub struct Email(String); // Newtype for validated input
impl Email {
pub fn new(s: &str) -> Result<Self, ValidationError> {
if validator::validate_email(s) { Ok(Self(s.to_string())) }
else { Err(ValidationError::InvalidEmail) }
}
}
// Load from environment or tauri-plugin-store with encryption
fn get_api_key() -> Result<String, AppError> {
std::env::var("API_KEY")
.map_err(|_| AppError::Configuration("API_KEY not set".into()))
}
See
references/security-examples.mdfor secure storage patterns
Bad : data.to_vec() then iterate - Good : Return iterator with lifetime
// Bad: fn process(data: &[u8]) -> Vec<u8> { data.to_vec().iter().map(|b| b+1).collect() }
fn process(data: &[u8]) -> impl Iterator<Item = u8> + '_ {
data.iter().map(|b| b + 1) // No allocation
}
Bad : Manual loop with push - Good : Iterator chain (lazy, fused)
fn filter_transform(items: &[Item]) -> Vec<String> {
items.iter().filter(|i| i.is_valid()).map(|i| i.name.to_uppercase()).collect()
}
Bad : Vec::with_capacity() in hot path - Good : Object pool
static BUFFER_POOL: Lazy<Pool<Vec<u8>>> = Lazy::new(|| Pool::new(32, || Vec::with_capacity(1024)));
async fn handle_request(data: &[u8]) -> Vec<u8> {
let mut buffer = BUFFER_POOL.pull(|| Vec::with_capacity(1024));
buffer.clear(); process(&mut buffer, data); buffer.to_vec()
}
Bad : CPU work on async - Good : spawn_blocking for CPU-bound
async fn hash_password(password: String) -> Result<String, AppError> {
tokio::task::spawn_blocking(move || {
argon2::hash_encoded(password.as_bytes(), &salt, &config)
.map_err(|e| AppError::Internal(e.into()))
}).await?
}
Bad : println! allocates - Good : write! to preallocated buffer
fn log_metric(buffer: &mut Vec<u8>, name: &str, value: u64) {
buffer.clear();
write!(buffer, "{}: {}", name, value).unwrap();
std::io::stdout().write_all(buffer).unwrap();
}
cargo audit # Dependency vulnerabilities
cargo +nightly careful test # Memory safety checking
cargo clippy -- -D warnings # Lint with security warnings
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_traversal_blocked() {
let base = Path::new("/app/data");
assert!(safe_path_join(base, "../etc/passwd").is_err());
assert!(safe_path_join(base, "user/file.txt").is_ok());
}
#[test]
fn test_command_allowlist() {
assert!(safe_command("rm", &["-rf", "/"]).is_err());
assert!(safe_command("git", &["status"]).is_ok());
}
}
See
references/advanced-patterns.mdfor fuzzing and integration test patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
.unwrap() in production | Panics crash app | Use ? with Result |
| Unsafe without docs | Unverified invariants | Add // SAFETY: comments |
| Shell command execution | Injection vulnerability | Use Command::new() directly |
| Ignoring Clippy | Missed security lints | Run cargo clippy -- -D warnings |
| Hardcoded credentials |
// NEVER: Shell injection
Command::new("sh").arg("-c").arg(format!("echo {}", user_input));
// ALWAYS: Direct execution
Command::new("echo").arg(user_input);
cargo audit// SAFETY: commentscargo test - all tests passcargo clippy -- -D warnings - no warningscargo audit - zero HIGH/CRITICAL vulnerabilitiesYour goal is to create Rust code that is:
Critical Security Reminders :
For detailed examples and advanced patterns, see the
references/directory
Weekly Installs
102
Repository
GitHub Stars
32
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode81
gemini-cli81
codex81
github-copilot78
cursor75
claude-code69
Flutter状态管理教程:MVVM与Provider实现单向数据流和架构模式
1,100 周安装
| Secrets in code |
| Use env vars or secure storage |