diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..040151f --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,46 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + name: build + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + +# clippy: +# name: clippy +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# - uses: actions-rs/toolchain@v1 +# with: +# profile: minimal +# toolchain: stable +# override: true +# components: clippy +# - uses: actions-rs/cargo@v1 +# with: +# command: clippy +# args: --workspace --all-targets -- --deny warnings +# +# fmt: +# name: fmt +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# - name: Run cargo fmt +# run: cargo fmt -- --check diff --git a/src/args.rs b/src/args.rs index b41b5b2..a8ba277 100644 --- a/src/args.rs +++ b/src/args.rs @@ -3,6 +3,7 @@ use pacman_bintrans_common::http::Proxy; use std::path::PathBuf; use structopt::StructOpt; use structopt::clap::AppSettings; +use url::Url; #[derive(Debug, StructOpt)] #[structopt(global_settings = &[AppSettings::ColoredHelp])] @@ -13,7 +14,7 @@ pub struct Args { #[structopt(short = "O", long)] pub output: PathBuf, #[structopt(long)] - pub transparency_url: Option, + pub transparency_url: Option, #[structopt(long)] pub pubkey: String, /// Example: socks5://127.0.0.1:9050 @@ -22,7 +23,7 @@ pub struct Args { /// Only use the proxy for transparency signatures, not the pkg #[structopt(long)] pub bypass_proxy_for_pkgs: bool, - pub url: String, + pub url: Url, } fn parse_proxy(proxy: &str) -> Result { diff --git a/src/main.rs b/src/main.rs index aad3144..106b3d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ use pacman_bintrans::proof; use pacman_bintrans_common::errors::*; use pacman_bintrans_common::http; use std::fs; -use std::path::Path; use std::rc::Rc; use structopt::StructOpt; use url::Url; @@ -29,6 +28,12 @@ fn needs_transparency_proof(url: &str) -> bool { iter.next() == Some(&"pkg") } +fn filename_from_url(url: &Url) -> Option { + let segments = url.path_segments()?; + let filename = segments.last()?; + Some(filename.to_string()) +} + #[tokio::main] async fn main() -> Result<()> { let args = Args::from_args(); @@ -46,30 +51,29 @@ async fn main() -> Result<()> { .context("Failed to load transparency public key")? .to_box()?; - let client = Rc::new(Client::new(args.proxy)?); + let client = Rc::new(Client::new(args.proxy.clone())?); let pkg_client = if args.bypass_proxy_for_pkgs { Rc::new(Client::new(None)?) } else { client.clone() }; - if needs_transparency_proof(&args.url) { + if needs_transparency_proof(&args.url.as_str()) { info!( "Transparency proof is required for {:?}, downloading into memory", - args.url + args.url.as_str() ); - let pkg = pkg_client.download_to_mem(&args.url, None).await?; + let pkg = pkg_client.download_to_mem(args.url.as_str(), None).await?; debug!("Downloaded {} bytes", pkg.len()); - let url = if let Some(url) = args.transparency_url { - let p = Path::new(&args.url); - let file_name = p.file_name() - .ok_or_else(|| anyhow!("Missing filename for url"))? - .to_str() - .ok_or_else(|| anyhow!("Invalid filename"))?; - let url = url.parse::()?; - let url = url.join(file_name)?; - url.as_str().to_string() + let url = if let Some(transparency_url) = &args.transparency_url { + let file_name = filename_from_url(&args.url) + .ok_or_else(|| anyhow!("Couldn't detect filename for url: {:?}", args.url.as_str()))?; + let mut url = transparency_url.clone(); + url.path_segments_mut() + .map_err(|_| anyhow!("Failed to get path segments for url"))? + .pop_if_empty().push(&file_name); + url } else { args.url }; @@ -81,8 +85,8 @@ async fn main() -> Result<()> { fs::write(args.output, &pkg).context("Failed to write database file after verification")?; debug!("Wrote {} bytes", pkg.len()); } else { - info!("Downloading {:?} to {:?}", args.url, args.output); - let n = client.download_to_file(&args.url, &args.output).await?; + info!("Downloading {:?} to {:?}", args.url.as_str(), args.output); + let n = client.download_to_file(args.url.as_str(), &args.output).await?; debug!("Downloaded {} bytes", n); } diff --git a/src/proof.rs b/src/proof.rs index 2b529be..4df54b5 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,5 +1,5 @@ -use pacman_bintrans_common::errors::*; use minisign::{PublicKeyBox, SignatureBox}; +use pacman_bintrans_common::errors::*; use pacman_bintrans_common::http::Client; use sha2::{Sha256, Digest}; use std::fs; @@ -8,6 +8,7 @@ use std::process::Stdio; use tempfile::NamedTempFile; use tokio::io::AsyncWriteExt; use tokio::process::Command; +use url::Url; const PROOF_SIZE_LIMIT: usize = 1024; // 1K @@ -69,11 +70,12 @@ pub async fn verify(pubkey: &PublicKeyBox, artifact: &[u8], sig: &[u8]) -> Resul Ok(()) } -pub async fn fetch_and_verify(client: &Client, pubkey: &PublicKeyBox, url: &str, pkg: &[u8]) -> Result<()> { - let url = format!("{}.t", url); +pub async fn fetch_and_verify(client: &Client, pubkey: &PublicKeyBox, url: &Url, pkg: &[u8]) -> Result<()> { + let url = format!("{}.t", url.as_str()); info!("Trying to download transparency proof from {:?}", url); + let url = url.parse::()?; - let proof = client.download_to_mem(&url, Some(PROOF_SIZE_LIMIT)).await?; + let proof = client.download_to_mem(url.as_str(), Some(PROOF_SIZE_LIMIT)).await?; debug!("Downloaded {} bytes", proof.len()); verify(&pubkey, pkg, &proof).await