Secret Scan / scan (push) Successful in 12s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m41s
Build App (Preview) / build-windows (pull_request) Successful in 4m51s
Build App (Preview) / build-linux (pull_request) Successful in 5m12s
Build App (Preview) / prune-previews (pull_request) Successful in 1s
The headline finding: WebTerminalSettings::access_token is a live bearer credential for a server that binds every interface, stored as a plain field on AppSettings — which this feature was exporting and importing wholesale as if it were as inert as a port number. A crafted export file could set web_terminal.enabled and access_token together, and importing it (with no more warning than any other setting change) would silently stand up a LAN-listening terminal server with an attacker-known token on the victim's next launch. Fixed by carving the token out into ExportedSecrets, same as the other three global secrets, with the same "only overwrite what the import actually has" treatment — except that has to be done by hand here, since this one lives inside the AppSettings blob that gets replaced wholesale rather than in the keychain. Added SettingsImportPreview:: enables_web_terminal so "this turns on a listening service" gets its own visible warning in the confirmation modal rather than hiding inside a generic "settings replaced" bullet list. Also fixed: - read_and_decrypt checked format_version only after attempting to parse the full payload, so a future version bump that isn't deserialize-compatible would fail on the shape mismatch before the version check ever ran — and serde's type-mismatch errors quote the offending value inline, which is a real leak path since the plaintext here can hold a live credential. Now probes just the version field first, and neither error path interpolates the underlying serde message into what the user sees. - apply_settings_import cleared the pending-import path before it could fail, so a rejected import (an invalid host path, anything update_settings validates) dead-ended the modal with no way back except cancelling and reopening the file picker. The path is now only cleared on success. - Secrets are restored before the settings replace runs, not after — replacing settings is what triggers reconcile_gateway, and restoring secrets afterward left a real window where a gateway recreation happened against the destination's stale keys. - The 8-character password minimum was frontend-only; export_settings now enforces it too, since that's the actual boundary a weak password has to cross. The derived key and decrypted plaintext are wrapped in zeroize::Zeroizing (already in the tree via aes-gcm). Added test coverage the review named as missing: format-version ordering, the generic-error-message guarantee, non_blank's blank-vs- absent handling, and the new web-terminal preview/warning behavior on both sides of the IPC boundary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ
54 lines
1.5 KiB
TOML
54 lines
1.5 KiB
TOML
[package]
|
|
name = "triple-c"
|
|
version = "0.4.0"
|
|
edition = "2021"
|
|
|
|
[lib]
|
|
name = "triple_c_lib"
|
|
crate-type = ["lib", "cdylib", "staticlib"]
|
|
|
|
[[bin]]
|
|
name = "triple-c"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
tauri = { version = "2", features = ["image-png", "image-ico"] }
|
|
tauri-plugin-dialog = "2"
|
|
tauri-plugin-opener = "2"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
bollard = "0.18"
|
|
keyring = { version = "3", features = ["apple-native", "windows-native", "linux-native"] }
|
|
tokio = { version = "1", features = ["full"] }
|
|
futures-util = "0.3"
|
|
uuid = { version = "1", features = ["v4"] }
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
dirs = "6"
|
|
log = "0.4"
|
|
fern = { version = "0.7", features = ["date-based"] }
|
|
tar = "0.4"
|
|
include_dir = "0.7"
|
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "multipart"] }
|
|
iana-time-zone = "0.1"
|
|
sha2 = "0.10"
|
|
axum = { version = "0.8", features = ["ws"] }
|
|
tower-http = { version = "0.6", features = ["cors"] }
|
|
base64 = "0.22"
|
|
rand = "0.9"
|
|
local-ip-address = "0.6"
|
|
argon2 = "0.5"
|
|
aes-gcm = "0.10"
|
|
zeroize = "1"
|
|
|
|
[dev-dependencies]
|
|
# `test-util` (not part of tokio's `full`) lets the auto-start retry tests run
|
|
# their backoff schedule under a paused clock instead of in real seconds.
|
|
tokio = { version = "1", features = ["full", "test-util"] }
|
|
|
|
[build-dependencies]
|
|
tauri-build = { version = "2", features = [] }
|
|
|
|
[features]
|
|
default = ["custom-protocol"]
|
|
custom-protocol = ["tauri/custom-protocol"]
|