feat: 客户端加密 encrypted 字段,数据库只存密文 (v0.5.0)
Some checks failed
Secrets CLI - Build & Release / 质量检查 (fmt / clippy / test) (push) Successful in 1m27s
Secrets CLI - Build & Release / 版本 & Release (push) Successful in 2s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Successful in 1m14s
Secrets CLI - Build & Release / 发布草稿 Release (push) Successful in 2s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 11m1s
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
Some checks failed
Secrets CLI - Build & Release / 质量检查 (fmt / clippy / test) (push) Successful in 1m27s
Secrets CLI - Build & Release / 版本 & Release (push) Successful in 2s
Secrets CLI - Build & Release / Build (x86_64-unknown-linux-musl) (push) Successful in 1m14s
Secrets CLI - Build & Release / 发布草稿 Release (push) Successful in 2s
Secrets CLI - Build & Release / Build (aarch64-apple-darwin) (push) Failing after 11m1s
Secrets CLI - Build & Release / Build (x86_64-pc-windows-msvc) (push) Has been cancelled
- 新增 src/crypto.rs:AES-256-GCM 加解密 + Argon2id 密钥派生 + OS Keychain 读写 - 新增 `secrets init` 命令:输入 Master Password,派生 Master Key 存入 Keychain - 新增 `secrets migrate-encrypt` 命令:将旧明文 JSONB 数据批量加密 - 修改 db.rs:encrypted 列 JSONB → BYTEA,新增 kv_config 表(存 Argon2id salt) - 修改 models.rs:encrypted 字段类型 Value → Vec<u8> - 修改 add/update:写入前 encrypt_json,update 读取后 decrypt → 合并 → 重新加密 - 修改 search:按需解密,未解密时显示 _encrypted:true/_key_count:N - 通过 6 个 crypto 单元测试(加解密、JSON roundtrip、Argon2id 确定性) Made-with: Cursor
This commit is contained in:
@@ -3,6 +3,7 @@ use serde_json::{Map, Value, json};
|
||||
use sqlx::PgPool;
|
||||
use std::fs;
|
||||
|
||||
use crate::crypto;
|
||||
use crate::output::OutputMode;
|
||||
|
||||
/// Parse "key=value" entries. Value starting with '@' reads from file.
|
||||
@@ -24,7 +25,7 @@ pub(crate) fn parse_kv(entry: &str) -> Result<(String, String)> {
|
||||
Ok((key.to_string(), value))
|
||||
}
|
||||
|
||||
fn build_json(entries: &[String]) -> Result<Value> {
|
||||
pub(crate) fn build_json(entries: &[String]) -> Result<Value> {
|
||||
let mut map = Map::new();
|
||||
for entry in entries {
|
||||
let (key, value) = parse_kv(entry)?;
|
||||
@@ -43,9 +44,12 @@ pub struct AddArgs<'a> {
|
||||
pub output: OutputMode,
|
||||
}
|
||||
|
||||
pub async fn run(pool: &PgPool, args: AddArgs<'_>) -> Result<()> {
|
||||
pub async fn run(pool: &PgPool, args: AddArgs<'_>, master_key: &[u8; 32]) -> Result<()> {
|
||||
let metadata = build_json(args.meta_entries)?;
|
||||
let encrypted = build_json(args.secret_entries)?;
|
||||
let secret_json = build_json(args.secret_entries)?;
|
||||
|
||||
// Encrypt the secret JSON before storing
|
||||
let encrypted_bytes = crypto::encrypt_json(master_key, &secret_json)?;
|
||||
|
||||
tracing::debug!(args.namespace, args.kind, args.name, "upserting record");
|
||||
|
||||
@@ -66,7 +70,7 @@ pub async fn run(pool: &PgPool, args: AddArgs<'_>) -> Result<()> {
|
||||
.bind(args.name)
|
||||
.bind(args.tags)
|
||||
.bind(&metadata)
|
||||
.bind(&encrypted)
|
||||
.bind(&encrypted_bytes)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user