Export drops threads; a broken keyring stops with the secret recoverable
This commit is contained in:
parent
96174c4776
commit
caa580fce9
|
|
@ -40,7 +40,7 @@ fn scrub(v: &mut Value) {
|
|||
pub fn export(b: &Bot) -> String {
|
||||
let mut v = serde_json::to_value(b).unwrap_or(Value::Null);
|
||||
if let Value::Object(m) = &mut v {
|
||||
for k in ["messages", "allow_rules", "unread", "last_turn", "workdir", "pinned", "id"] {
|
||||
for k in ["messages", "threads", "allow_rules", "unread", "last_turn", "workdir", "pinned", "id"] {
|
||||
m.remove(k);
|
||||
}
|
||||
if let Some(Value::Array(routines)) = m.get_mut("routines") {
|
||||
|
|
|
|||
15
src/vault.rs
15
src/vault.rs
|
|
@ -178,7 +178,10 @@ fn onepassword(host: &str) -> Option<Login> {
|
|||
return None;
|
||||
}
|
||||
let items: Value = serde_json::from_slice(&list.stdout).ok()?;
|
||||
let id = items.as_array()?.iter().find(|i| i["urls"].as_array().into_iter().flatten().any(|u| host_of(u["href"].as_str().unwrap_or("")).ends_with(host)))?["id"].as_str()?.to_string();
|
||||
// Match the host exactly or as a subdomain (with the dot): "notgoogle.com" must not
|
||||
// answer for "google.com" (a plain ends_with fills the wrong login on a lookalike domain).
|
||||
let host_match = |h: &str, want: &str| h == want || h.ends_with(&format!(".{want}"));
|
||||
let id = items.as_array()?.iter().find(|i| i["urls"].as_array().into_iter().flatten().any(|u| host_match(&host_of(u["href"].as_str().unwrap_or("")), host)))?["id"].as_str()?.to_string();
|
||||
let got = Command::new("op").env("PATH", &path).args(["item", "get", &id, "--fields", "label=username,label=password", "--reveal", "--format", "json"]).output().ok()?;
|
||||
let fields: Value = serde_json::from_slice(&got.stdout).ok()?;
|
||||
let arr = fields.as_array()?;
|
||||
|
|
@ -412,9 +415,6 @@ pub fn fill_login(plan: &LoginPlan, submit: bool) -> Result<String, String> {
|
|||
let (username, password, source) = match (&plan.item, &plan.other) {
|
||||
(Some(i), _) => {
|
||||
let (u, p) = crate::vault_store::login_secret(&i.id).ok_or("保險庫裡沒有這筆登入資訊")?;
|
||||
if !plan.matched {
|
||||
crate::vault_store::add_site(&i.id, &plan.host)?;
|
||||
}
|
||||
(u, p, "保險庫")
|
||||
}
|
||||
(None, Some(l)) => (l.username.clone(), l.password.clone(), l.source),
|
||||
|
|
@ -425,6 +425,13 @@ pub fn fill_login(plan: &LoginPlan, submit: bool) -> Result<String, String> {
|
|||
if got["password"].as_bool() != Some(true) && got["user"].as_bool() != Some(true) {
|
||||
return Err("這一頁找不到帳號或密碼欄位。先打開登入頁(有些網站要先按「登入」或先填帳號、按下一步才出現密碼欄)。".into());
|
||||
}
|
||||
// Remember the domain only after the fill worked: adding it on a failed (or lookalike)
|
||||
// page made the next visit show matched:true and skip the anti-phishing warning.
|
||||
if !plan.matched {
|
||||
if let Some(i) = &plan.item {
|
||||
crate::vault_store::add_site(&i.id, &plan.host)?;
|
||||
}
|
||||
}
|
||||
Ok(format!(
|
||||
"已在 {} 填入{}{}(來自{source},你看不到密碼)。{}",
|
||||
plan.host,
|
||||
|
|
|
|||
|
|
@ -381,8 +381,12 @@ fn store_key(key: &[u8]) -> Result<(), String> {
|
|||
fn key(create: bool) -> Result<aead::LessSafeKey, String> {
|
||||
let k = match read_key() {
|
||||
Some(k) if k.len() == 32 => k,
|
||||
_ if !create => return Err("找不到保險庫的金鑰(系統鑰匙圈裡的「AnyBot vault key」)".into()),
|
||||
_ => {
|
||||
// An entry exists but is not a key: generating a fresh one (and -U overwriting the
|
||||
// entry) would destroy the only copy of the old key — every saved password would be
|
||||
// undecryptable forever. Stop and tell the person instead.
|
||||
Some(_) => return Err("保險庫金鑰損毀:鑰匙圈裡的「AnyBot vault key」內容不正確。已停止動作,避免覆蓋後舊密碼永遠解不開;若你確定不需要舊密碼,可手動刪除該鑰匙圈項目再試。".into()),
|
||||
None if !create => return Err("找不到保險庫的金鑰(系統鑰匙圈裡的「AnyBot vault key」)".into()),
|
||||
None => {
|
||||
let mut k = vec![0u8; 32];
|
||||
SystemRandom::new().fill(&mut k).map_err(|_| "產生金鑰失敗")?;
|
||||
store_key(&k)?;
|
||||
|
|
|
|||
Loading…
Reference in New Issue