@@ -0,0 +1 @@
|
||||
/target
|
||||
Generated
+68
@@ -0,0 +1,68 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitwarden-proton-sync"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fancy-regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "bitwarden-proton-sync"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
fancy-regex = "0.18.0"
|
||||
@@ -0,0 +1,13 @@
|
||||
# bitwarden-proton-sync
|
||||
|
||||
This is a cron-based CLI tool to sync Bitwarden to Proton Pass.
|
||||
|
||||
### Runtime requirements:
|
||||
|
||||
- `pass-cli` - Proton Pass CLI (must be logged in)
|
||||
|
||||
### Building
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
```
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
mod pass;
|
||||
mod sh;
|
||||
|
||||
use std::env;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
const ENV_VAR_DEFAULT_VAULT: &str = "PASS_VAULT";
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
pass::check_pass()?;
|
||||
let vaults = pass::get_vaults()?;
|
||||
if vaults.len() <= 0 {
|
||||
return Err(Error::new(ErrorKind::Other, "No vaults found"));
|
||||
}
|
||||
|
||||
let mut vault = String::new();
|
||||
match env::var(ENV_VAR_DEFAULT_VAULT) {
|
||||
Ok(default_vault) => {
|
||||
if vaults.contains(&default_vault) {
|
||||
vault = default_vault;
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Vault {} does not exist", default_vault),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
vault = vaults.get(0).unwrap().clone();
|
||||
println!("No default vault found (you can set it with {}), using {}...", ENV_VAR_DEFAULT_VAULT, vault);
|
||||
}
|
||||
};
|
||||
println!("selected: {:?}", vault);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
use crate::sh;
|
||||
use fancy_regex::Regex;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
const EXECUTABLE: &str = "pass-cli";
|
||||
const VAULT_NAME_REGEX: &str = r"(?m)(?<=- \[.{88}\]: ).*$";
|
||||
|
||||
pub fn check_pass() -> Result<(), Error> {
|
||||
let which = sh::sh(format!("which {}", EXECUTABLE));
|
||||
if which.is_empty() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("{} is not installed", EXECUTABLE),
|
||||
));
|
||||
}
|
||||
|
||||
let test = sh::sh(format!("{} test", EXECUTABLE));
|
||||
if test != "Connection successful\n" {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("{} test failed", EXECUTABLE),
|
||||
));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn get_vaults() -> Result<Vec<String>, Error> {
|
||||
let re = Regex::new(VAULT_NAME_REGEX).unwrap();
|
||||
let vaults_raw = sh::sh(format!("{} vault list", EXECUTABLE));
|
||||
|
||||
let captures = re
|
||||
.captures(vaults_raw.as_str())
|
||||
.expect("Error running regex");
|
||||
if let None = captures {
|
||||
return Err(Error::new(ErrorKind::Other, "No vault found"));
|
||||
}
|
||||
|
||||
let mut vaults = Vec::<String>::new();
|
||||
|
||||
let captures = captures.unwrap();
|
||||
for each in captures.iter() {
|
||||
if let None = each {
|
||||
continue;
|
||||
}
|
||||
|
||||
let each = each.unwrap();
|
||||
let vault = each.as_str();
|
||||
if !vault.is_empty() {
|
||||
vaults.push(vault.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(vaults);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use std::process::Command;
|
||||
|
||||
pub fn sh(command: impl Into<String>) -> String {
|
||||
let output = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(command.into())
|
||||
.output()
|
||||
.expect("Failed to execute command");
|
||||
|
||||
return String::from_utf8(output.stdout).expect("Invalid UTF-8 sequence");
|
||||
}
|
||||
Reference in New Issue
Block a user