mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-01 10:01:07 +02:00
This commit is contained in:
committed by
GitHub
parent
3c4ee7c997
commit
dbc2873e82
5
.changes/improve-updater-validation.md
Normal file
5
.changes/improve-updater-validation.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": patch
|
||||
---
|
||||
|
||||
Improved the updater response validation and error messages.
|
||||
@@ -51,7 +51,7 @@ pub struct RemoteRelease {
|
||||
/// Update short description
|
||||
pub body: Option<String>,
|
||||
/// Optional signature for the current platform
|
||||
pub signature: Option<String>,
|
||||
pub signature: String,
|
||||
#[cfg(target_os = "windows")]
|
||||
/// Optional: Windows only try to use elevated task
|
||||
pub with_elevated_task: bool,
|
||||
@@ -65,42 +65,47 @@ impl RemoteRelease {
|
||||
let version = match release.get("version") {
|
||||
Some(version) => version
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
Error::RemoteMetadata("Unable to extract `version` from remote server".into())
|
||||
})?
|
||||
.trim_start_matches('v')
|
||||
.to_string(),
|
||||
None => release
|
||||
.get("name")
|
||||
.ok_or_else(|| Error::RemoteMetadata("Release missing `name` and `version`".into()))?
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
Error::RemoteMetadata("Unable to extract `name` from remote server`".into())
|
||||
})?
|
||||
.ok_or_else(|| Error::InvalidResponseType("version", "string", version.clone()))?
|
||||
.trim_start_matches('v')
|
||||
.to_string(),
|
||||
None => {
|
||||
let name = release
|
||||
.get("name")
|
||||
.ok_or(Error::MissingResponseField("version or name"))?;
|
||||
name
|
||||
.as_str()
|
||||
.ok_or_else(|| Error::InvalidResponseType("name", "string", name.clone()))?
|
||||
.trim_start_matches('v')
|
||||
.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
// pub_date is required default is: `N/A` if not provided by the remote JSON
|
||||
let date = release
|
||||
.get("pub_date")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("N/A")
|
||||
.to_string();
|
||||
let date = if let Some(date) = release.get("pub_date") {
|
||||
date
|
||||
.as_str()
|
||||
.map(|d| d.to_string())
|
||||
.ok_or_else(|| Error::InvalidResponseType("pub_date", "string", date.clone()))?
|
||||
} else {
|
||||
"N/A".into()
|
||||
};
|
||||
|
||||
// body is optional to build our update
|
||||
let body = release
|
||||
.get("notes")
|
||||
.map(|notes| notes.as_str().unwrap_or("").to_string());
|
||||
|
||||
// signature is optional to build our update
|
||||
let mut signature = release
|
||||
.get("signature")
|
||||
.map(|signature| signature.as_str().unwrap_or("").to_string());
|
||||
let body = if let Some(notes) = release.get("notes") {
|
||||
Some(
|
||||
notes
|
||||
.as_str()
|
||||
.map(|n| n.to_string())
|
||||
.ok_or_else(|| Error::InvalidResponseType("notes", "string", notes.clone()))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let download_url;
|
||||
#[cfg(target_os = "windows")]
|
||||
let with_elevated_task;
|
||||
let signature;
|
||||
|
||||
match release.get("platforms") {
|
||||
//
|
||||
@@ -123,38 +128,53 @@ impl RemoteRelease {
|
||||
// use provided signature if available
|
||||
signature = current_target_data
|
||||
.get("signature")
|
||||
.map(|found_signature| found_signature.as_str().unwrap_or("").to_string());
|
||||
.ok_or(Error::MissingResponseField("signature"))
|
||||
.and_then(|signature| {
|
||||
signature
|
||||
.as_str()
|
||||
.ok_or_else(|| Error::InvalidResponseType("signature", "string", signature.clone()))
|
||||
})?;
|
||||
// Download URL is required
|
||||
download_url = current_target_data
|
||||
let url = current_target_data
|
||||
.get("url")
|
||||
.ok_or_else(|| Error::RemoteMetadata("Release missing `url`".into()))?
|
||||
.ok_or(Error::MissingResponseField("url"))?;
|
||||
download_url = url
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
Error::RemoteMetadata("Unable to extract `url` from remote server`".into())
|
||||
})?
|
||||
.ok_or_else(|| Error::InvalidResponseType("url", "string", url.clone()))?
|
||||
.to_string();
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
with_elevated_task = current_target_data
|
||||
.get("with_elevated_task")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or_default();
|
||||
.map(|v| {
|
||||
v.as_bool().ok_or_else(|| {
|
||||
Error::InvalidResponseType("with_elevated_task", "boolean", v.clone())
|
||||
})
|
||||
})
|
||||
.unwrap_or(Ok(false))?;
|
||||
}
|
||||
} else {
|
||||
// make sure we have an available platform from the static
|
||||
return Err(Error::RemoteMetadata("Platform not available".into()));
|
||||
return Err(Error::TargetNotFound(target.into()));
|
||||
}
|
||||
}
|
||||
// We don't have the `platforms` field announced, let's assume our
|
||||
// download URL is at the root of the JSON.
|
||||
None => {
|
||||
download_url = release
|
||||
signature = release
|
||||
.get("signature")
|
||||
.ok_or(Error::MissingResponseField("signature"))
|
||||
.and_then(|signature| {
|
||||
signature
|
||||
.as_str()
|
||||
.ok_or_else(|| Error::InvalidResponseType("signature", "string", signature.clone()))
|
||||
})?;
|
||||
let url = release
|
||||
.get("url")
|
||||
.ok_or_else(|| Error::RemoteMetadata("Release missing `url`".into()))?
|
||||
.ok_or(Error::MissingResponseField("url"))?;
|
||||
download_url = url
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
Error::RemoteMetadata("Unable to extract `url` from remote server`".into())
|
||||
})?
|
||||
.ok_or_else(|| Error::InvalidResponseType("url", "string", url.clone()))?
|
||||
.to_string();
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -171,7 +191,7 @@ impl RemoteRelease {
|
||||
date,
|
||||
download_url,
|
||||
body,
|
||||
signature,
|
||||
signature: signature.to_string(),
|
||||
#[cfg(target_os = "windows")]
|
||||
with_elevated_task,
|
||||
})
|
||||
@@ -356,13 +376,11 @@ impl<'a, R: Runtime> UpdateBuilder<'a, R> {
|
||||
// Last error is cleaned on success -- shouldn't be triggered if
|
||||
// we have a successful call
|
||||
if let Some(error) = last_error {
|
||||
return Err(Error::Network(error.to_string()));
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
// Extracted remote metadata
|
||||
let final_release = remote_release.ok_or_else(|| {
|
||||
Error::RemoteMetadata("Unable to extract update metadata from the remote server.".into())
|
||||
})?;
|
||||
let final_release = remote_release.ok_or(Error::ReleaseNotFound)?;
|
||||
|
||||
// did the announced version is greated than our current one?
|
||||
let should_update =
|
||||
@@ -412,7 +430,7 @@ pub struct Update<R: Runtime> {
|
||||
/// Download URL announced
|
||||
download_url: String,
|
||||
/// Signature announced
|
||||
signature: Option<String>,
|
||||
signature: String,
|
||||
#[cfg(target_os = "windows")]
|
||||
/// Optional: Windows only try to use elevated task
|
||||
/// Default to false
|
||||
@@ -521,14 +539,7 @@ impl<R: Runtime> Update<R> {
|
||||
|
||||
// We need an announced signature by the server
|
||||
// if there is no signature, bail out.
|
||||
if let Some(signature) = &self.signature {
|
||||
// we make sure the archive is valid and signed with the private key linked with the publickey
|
||||
verify_signature(&mut archive_buffer, signature, &pub_key)?;
|
||||
} else {
|
||||
// We have a public key inside our source file, but not announced by the server,
|
||||
// we assume this update is NOT valid.
|
||||
return Err(Error::MissingUpdaterSignature);
|
||||
}
|
||||
verify_signature(&mut archive_buffer, &self.signature, &pub_key)?;
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
{
|
||||
@@ -816,7 +827,9 @@ pub fn extract_path_from_executable(env: &Env, executable_path: &Path) -> PathBu
|
||||
// Convert base64 to string and prevent failing
|
||||
fn base64_to_string(base64_string: &str) -> Result<String> {
|
||||
let decoded_string = &decode(base64_string)?;
|
||||
let result = from_utf8(decoded_string)?.to_string();
|
||||
let result = from_utf8(decoded_string)
|
||||
.map_err(|_| Error::SignatureUtf8(base64_string.into()))?
|
||||
.to_string();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -868,19 +881,19 @@ mod test {
|
||||
"platforms": {
|
||||
"darwin-aarch64": {
|
||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJZVGdpKzJmRWZ0SkRvWS9TdFpqTU9xcm1mUmJSSG5OWVlwSklrWkN1SFpWbmh4SDlBcTU3SXpjbm0xMmRjRkphbkpVeGhGcTdrdzlrWGpGVWZQSWdzPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1MDU3CWZpbGU6L1VzZXJzL3J1bm5lci9ydW5uZXJzLzIuMjYzLjAvd29yay90YXVyaS90YXVyaS90YXVyaS9leGFtcGxlcy9jb21tdW5pY2F0aW9uL3NyYy10YXVyaS90YXJnZXQvZGVidWcvYnVuZGxlL29zeC9hcHAuYXBwLnRhci5negp4ZHFlUkJTVnpGUXdDdEhydTE5TGgvRlVPeVhjTnM5RHdmaGx3c0ZPWjZXWnFwVDRNWEFSbUJTZ1ZkU1IwckJGdmlwSzJPd00zZEZFN2hJOFUvL1FDZz09Cg==",
|
||||
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.app.tar.gz"
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v1.0.0/app.app.tar.gz"
|
||||
},
|
||||
"darwin-x86_64": {
|
||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJZVGdpKzJmRWZ0SkRvWS9TdFpqTU9xcm1mUmJSSG5OWVlwSklrWkN1SFpWbmh4SDlBcTU3SXpjbm0xMmRjRkphbkpVeGhGcTdrdzlrWGpGVWZQSWdzPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1MDU3CWZpbGU6L1VzZXJzL3J1bm5lci9ydW5uZXJzLzIuMjYzLjAvd29yay90YXVyaS90YXVyaS90YXVyaS9leGFtcGxlcy9jb21tdW5pY2F0aW9uL3NyYy10YXVyaS90YXJnZXQvZGVidWcvYnVuZGxlL29zeC9hcHAuYXBwLnRhci5negp4ZHFlUkJTVnpGUXdDdEhydTE5TGgvRlVPeVhjTnM5RHdmaGx3c0ZPWjZXWnFwVDRNWEFSbUJTZ1ZkU1IwckJGdmlwSzJPd00zZEZFN2hJOFUvL1FDZz09Cg==",
|
||||
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.app.tar.gz"
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v1.0.0/app.app.tar.gz"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOWZSM29hTFNmUEdXMHRoOC81WDFFVVFRaXdWOUdXUUdwT0NlMldqdXkyaWVieXpoUmdZeXBJaXRqSm1YVmczNXdRL1Brc0tHb1NOTzhrL1hadFcxdmdnPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE3MzQzCWZpbGU6L2hvbWUvcnVubmVyL3dvcmsvdGF1cmkvdGF1cmkvdGF1cmkvZXhhbXBsZXMvY29tbXVuaWNhdGlvbi9zcmMtdGF1cmkvdGFyZ2V0L2RlYnVnL2J1bmRsZS9hcHBpbWFnZS9hcHAuQXBwSW1hZ2UudGFyLmd6CmRUTUM2bWxnbEtTbUhOZGtERUtaZnpUMG5qbVo5TGhtZWE1SFNWMk5OOENaVEZHcnAvVW0zc1A2ajJEbWZUbU0yalRHT0FYYjJNVTVHOHdTQlYwQkF3PT0K",
|
||||
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.AppImage.tar.gz"
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v1.0.0/app.AppImage.tar.gz"
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJHMWlvTzRUSlQzTHJOMm5waWpic0p0VVI2R0hUNGxhQVMxdzBPRndlbGpXQXJJakpTN0toRURtVzBkcm15R0VaNTJuS1lZRWdzMzZsWlNKUVAzZGdJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1NTIzCWZpbGU6RDpcYVx0YXVyaVx0YXVyaVx0YXVyaVxleGFtcGxlc1xjb21tdW5pY2F0aW9uXHNyYy10YXVyaVx0YXJnZXRcZGVidWdcYXBwLng2NC5tc2kuemlwCitXa1lQc3A2MCs1KzEwZnVhOGxyZ2dGMlZqbjBaVUplWEltYUdyZ255eUF6eVF1dldWZzFObStaVEQ3QU1RS1lzcjhDVU4wWFovQ1p1QjJXbW1YZUJ3PT0K",
|
||||
"url": "https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.x64.msi.zip"
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v1.0.0/app.x64.msi.zip"
|
||||
}
|
||||
}
|
||||
}"#.into()
|
||||
@@ -926,15 +939,6 @@ mod test {
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_sample_bad_json() -> String {
|
||||
r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"date": "2020-02-20T15:41:00Z",
|
||||
"download_link": "https://github.com/lemarier/tauri-test/releases/download/v0.0.1/update3.tar.gz"
|
||||
}"#.into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_http_updater() {
|
||||
let _m = mockito::mock("GET", "/")
|
||||
@@ -992,10 +996,10 @@ mod test {
|
||||
|
||||
assert!(updater.should_update);
|
||||
assert_eq!(updater.version, "2.0.0");
|
||||
assert_eq!(updater.signature, Some("dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJHMWlvTzRUSlQzTHJOMm5waWpic0p0VVI2R0hUNGxhQVMxdzBPRndlbGpXQXJJakpTN0toRURtVzBkcm15R0VaNTJuS1lZRWdzMzZsWlNKUVAzZGdJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1NTIzCWZpbGU6RDpcYVx0YXVyaVx0YXVyaVx0YXVyaVxleGFtcGxlc1xjb21tdW5pY2F0aW9uXHNyYy10YXVyaVx0YXJnZXRcZGVidWdcYXBwLng2NC5tc2kuemlwCitXa1lQc3A2MCs1KzEwZnVhOGxyZ2dGMlZqbjBaVUplWEltYUdyZ255eUF6eVF1dldWZzFObStaVEQ3QU1RS1lzcjhDVU4wWFovQ1p1QjJXbW1YZUJ3PT0K".into()));
|
||||
assert_eq!(updater.signature, "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldUTE5QWWxkQnlZOVJHMWlvTzRUSlQzTHJOMm5waWpic0p0VVI2R0hUNGxhQVMxdzBPRndlbGpXQXJJakpTN0toRURtVzBkcm15R0VaNTJuS1lZRWdzMzZsWlNKUVAzZGdJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNTkyOTE1NTIzCWZpbGU6RDpcYVx0YXVyaVx0YXVyaVx0YXVyaVxleGFtcGxlc1xjb21tdW5pY2F0aW9uXHNyYy10YXVyaVx0YXJnZXRcZGVidWdcYXBwLng2NC5tc2kuemlwCitXa1lQc3A2MCs1KzEwZnVhOGxyZ2dGMlZqbjBaVUplWEltYUdyZ255eUF6eVF1dldWZzFObStaVEQ3QU1RS1lzcjhDVU4wWFovQ1p1QjJXbW1YZUJ3PT0K");
|
||||
assert_eq!(
|
||||
updater.download_url,
|
||||
"https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.x64.msi.zip"
|
||||
"https://github.com/tauri-apps/updater-test/releases/download/v1.0.0/app.x64.msi.zip"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1181,21 +1185,227 @@ mod test {
|
||||
assert!(updater.should_update);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn http_updater_invalid_remote_data() {
|
||||
let invalid_signature = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": true
|
||||
}"#;
|
||||
let invalid_version = r#"{
|
||||
"version": 5,
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let invalid_name = r#"{
|
||||
"name": false,
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let invalid_date = r#"{
|
||||
"version": "1.0.0",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": 345645646,
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let invalid_notes = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": ["bla", "bla"],
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let invalid_url = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": ["https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz", "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz"],
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let invalid_platform_signature = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"platforms": {
|
||||
"test-target": {
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": {
|
||||
"test-target": "x"
|
||||
}
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
let invalid_platform_url = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"platforms": {
|
||||
"test-target": {
|
||||
"url": {
|
||||
"first": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz"
|
||||
}
|
||||
"signature": "x"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
let test_cases = [
|
||||
(
|
||||
invalid_signature,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("signature", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_version,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("version", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_name,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("name", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_date,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("pub_date", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_notes,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("notes", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_url,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("url", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_platform_signature,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("signature", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
(
|
||||
invalid_platform_url,
|
||||
Box::new(|e| matches!(e, Error::InvalidResponseType("url", "string", _)))
|
||||
as Box<dyn FnOnce(Error) -> bool>,
|
||||
),
|
||||
];
|
||||
|
||||
for (response, validator) in test_cases {
|
||||
let _m = mockito::mock("GET", "/")
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(response)
|
||||
.create();
|
||||
|
||||
let app = crate::test::mock_app();
|
||||
let check_update = block!(builder(app.handle())
|
||||
.url(mockito::server_url())
|
||||
.current_version("0.0.1")
|
||||
.target("test-target")
|
||||
.build());
|
||||
if let Err(e) = check_update {
|
||||
validator(e);
|
||||
} else {
|
||||
panic!("unexpected Ok response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn http_updater_missing_remote_data() {
|
||||
let _m = mockito::mock("GET", "/")
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(generate_sample_bad_json())
|
||||
.create();
|
||||
let missing_signature = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz"
|
||||
}"#;
|
||||
let missing_version = r#"{
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let missing_url = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"signature": "x"
|
||||
}"#;
|
||||
let missing_target = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"platforms": {
|
||||
"unknown-target": {
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz",
|
||||
"signature": "x"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
let missing_platform_signature = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"platforms": {
|
||||
"test-target": {
|
||||
"url": "https://github.com/tauri-apps/updater-test/releases/download/v0.0.1/update3.tar.gz"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
let missing_platform_url = r#"{
|
||||
"version": "v0.0.3",
|
||||
"notes": "Blablaa",
|
||||
"pub_date": "2020-02-20T15:41:00Z",
|
||||
"platforms": {
|
||||
"test-target": {
|
||||
"signature": "x"
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
|
||||
let app = crate::test::mock_app();
|
||||
let check_update = block!(builder(app.handle())
|
||||
.url(mockito::server_url())
|
||||
.current_version("0.0.1")
|
||||
.build());
|
||||
let test_cases = [
|
||||
(missing_signature, Error::MissingResponseField("signature")),
|
||||
(
|
||||
missing_version,
|
||||
Error::MissingResponseField("version or name"),
|
||||
),
|
||||
(missing_url, Error::MissingResponseField("url")),
|
||||
(missing_target, Error::TargetNotFound("test-target".into())),
|
||||
(
|
||||
missing_platform_signature,
|
||||
Error::MissingResponseField("signature"),
|
||||
),
|
||||
(missing_platform_url, Error::MissingResponseField("url")),
|
||||
];
|
||||
|
||||
assert!(check_update.is_err());
|
||||
for (response, error) in test_cases {
|
||||
let _m = mockito::mock("GET", "/")
|
||||
.with_status(200)
|
||||
.with_header("content-type", "application/json")
|
||||
.with_body(response)
|
||||
.create();
|
||||
|
||||
let app = crate::test::mock_app();
|
||||
let check_update = block!(builder(app.handle())
|
||||
.url(mockito::server_url())
|
||||
.current_version("0.0.1")
|
||||
.target("test-target")
|
||||
.build());
|
||||
if let Err(e) = check_update {
|
||||
assert_eq!(e.to_string(), error.to_string());
|
||||
} else {
|
||||
panic!("unexpected Ok response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run complete process on mac only for now as we don't have
|
||||
|
||||
@@ -24,17 +24,17 @@ pub enum Error {
|
||||
#[error("Signature decoding error: {0}")]
|
||||
Base64(#[from] base64::DecodeError),
|
||||
/// UTF8 Errors in signature.
|
||||
#[error("Signature encoding error: {0}")]
|
||||
Utf8(#[from] std::str::Utf8Error),
|
||||
#[error("The signature {0} could not be decoded, please check if it is a valid base64 string. The signature must be the contents of the `.sig` file generated by the Tauri bundler, as a string.")]
|
||||
SignatureUtf8(String),
|
||||
/// Tauri utils, mainly extract and file move.
|
||||
#[error("Tauri API error: {0}")]
|
||||
TauriApi(#[from] crate::api::Error),
|
||||
/// Network error.
|
||||
#[error("Network error: {0}")]
|
||||
Network(String),
|
||||
/// Metadata (JSON) error.
|
||||
#[error("Remote JSON error: {0}")]
|
||||
RemoteMetadata(String),
|
||||
/// Could not fetch a valid response from the server.
|
||||
#[error("Could not fetch a valid release JSON from the remote")]
|
||||
ReleaseNotFound,
|
||||
/// Error building updater.
|
||||
#[error("Unable to prepare the updater: {0}")]
|
||||
Builder(String),
|
||||
@@ -44,13 +44,19 @@ pub enum Error {
|
||||
/// Updater is not supported for current operating system or platform.
|
||||
#[error("Unsupported operating system or platform")]
|
||||
UnsupportedPlatform,
|
||||
/// Public key found in `tauri.conf.json` but no signature announced remotely.
|
||||
#[error("Signature not available, skipping update")]
|
||||
MissingUpdaterSignature,
|
||||
/// The platform was not found on the updater JSON response.
|
||||
#[error("the platform `{0}` was not found on the response `platforms` object")]
|
||||
TargetNotFound(String),
|
||||
/// Triggered when there is NO error and the two versions are equals.
|
||||
/// On client side, it's important to catch this error.
|
||||
#[error("No updates available")]
|
||||
UpToDate,
|
||||
/// The server did not include the signature field.
|
||||
#[error("the `{0}` field was not set on the updater response")]
|
||||
MissingResponseField(&'static str),
|
||||
/// The updater responded with an invalid signature type.
|
||||
#[error("the updater response field `{0}` type is invalid, expected {1} but found {2}")]
|
||||
InvalidResponseType(&'static str, &'static str, serde_json::Value),
|
||||
}
|
||||
|
||||
pub type Result<T = ()> = std::result::Result<T, Error>;
|
||||
|
||||
Reference in New Issue
Block a user