diff --git a/package.json b/package.json index a50eba1..d1ab275 100644 --- a/package.json +++ b/package.json @@ -138,19 +138,19 @@ }, "pnpm": { "overrides": { - "@lhci/cli>tmp": "^0.2.4", + "@lhci/cli>tmp": "^0.2.6", "basic-ftp": "^5.3.0", "commitizen>lodash": "^4.17.23", "eslint>ajv": "^6.14.0", - "external-editor>tmp": "^0.2.4", + "external-editor>tmp": "^0.2.6", "flatted": "^3.4.2", "minimatch": "^10.2.3", "rollup": "^4.59.0", - "tmp": "^0.2.4", + "tmp": "^0.2.6", "yauzl": "^3.2.1" }, "onlyBuiltDependencies": [ "esbuild" ] } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84555e2..21c5d21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,15 +5,15 @@ settings: excludeLinksFromLockfile: false overrides: - '@lhci/cli>tmp': ^0.2.4 + '@lhci/cli>tmp': ^0.2.6 basic-ftp: ^5.3.0 commitizen>lodash: ^4.17.23 eslint>ajv: ^6.14.0 - external-editor>tmp: ^0.2.4 + external-editor>tmp: ^0.2.6 flatted: ^3.4.2 minimatch: ^10.2.3 rollup: ^4.59.0 - tmp: ^0.2.4 + tmp: ^0.2.6 yauzl: ^3.2.1 importers: @@ -3768,8 +3768,8 @@ packages: resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} hasBin: true - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + tmp@0.2.6: + resolution: {integrity: sha512-5sJPdPjfI5Kx+qbrDesxkglRBxW//g7hCsqspEjwkewGvBMGIKMOTKzLt1hFVJzyadba3lDUN20O9qhvbQUSTA==} engines: {node: '>=14.14'} to-regex-range@5.0.1: @@ -4726,7 +4726,7 @@ snapshots: lighthouse-logger: 1.2.0 open: 7.4.2 proxy-agent: 6.5.0 - tmp: 0.2.5 + tmp: 0.2.6 uuid: 8.3.2 yargs: 15.4.1 yargs-parser: 13.1.2 @@ -6356,7 +6356,7 @@ snapshots: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 - tmp: 0.2.5 + tmp: 0.2.6 extract-zip@2.0.1: dependencies: @@ -8052,7 +8052,7 @@ snapshots: dependencies: tldts-core: 7.0.30 - tmp@0.2.5: {} + tmp@0.2.6: {} to-regex-range@5.0.1: dependencies: @@ -8375,4 +8375,4 @@ snapshots: yocto-queue@1.2.2: {} - zod@3.25.76: {} + zod@3.25.76: {} \ No newline at end of file diff --git a/scripts/security/run-cargo-audit.sh b/scripts/security/run-cargo-audit.sh index 734cc5a..fda5fea 100644 --- a/scripts/security/run-cargo-audit.sh +++ b/scripts/security/run-cargo-audit.sh @@ -10,8 +10,8 @@ cd "$repo_root/src-tauri" # Owner: Platform Engineering # Review date: 2026-03-09 # Last remediation update: hickory-resolver upgraded to 0.26.1 to clear -# RUSTSEC-2026-0119, and lz4_flex upgraded to 0.12.1 to clear -# RUSTSEC-2026-0041. +# RUSTSEC-2026-0119, and locked lz4_flex versions kept outside the +# RUSTSEC-2026-0041 vulnerable ranges. # Umbrella tracking issue: https://github.com/saagar210/AssistSupport/issues/11 # # GTK3/Tauri Linux runtime chain (issue #12): @@ -65,4 +65,4 @@ cargo audit --deny unsound --deny unmaintained \ --ignore RUSTSEC-2025-0100 \ --ignore RUSTSEC-2026-0002 \ --ignore RUSTSEC-2026-0097 \ - --ignore RUSTSEC-2026-0105 + --ignore RUSTSEC-2026-0105 \ No newline at end of file diff --git a/src-tauri/tests/security_dependency_versions.rs b/src-tauri/tests/security_dependency_versions.rs new file mode 100644 index 0000000..c321975 --- /dev/null +++ b/src-tauri/tests/security_dependency_versions.rs @@ -0,0 +1,110 @@ +//! Dependency pinning checks for security-alert remediations. + +use std::fs; + +fn cargo_lock() -> String { + fs::read_to_string(format!("{}/Cargo.lock", env!("CARGO_MANIFEST_DIR"))) + .expect("Cargo.lock should be readable") +} + +fn package_versions<'a>(lockfile: &'a str, package_name: &str) -> Vec<&'a str> { + let versions: Vec<_> = lockfile + .split("[[package]]") + .filter_map(|package| { + let mut name = None; + let mut version = None; + + for line in package.lines() { + if let Some(value) = line.strip_prefix("name = \"") { + name = value.strip_suffix('"'); + } + + if let Some(value) = line.strip_prefix("version = \"") { + version = value.strip_suffix('"'); + } + } + + (name == Some(package_name)).then_some(version).flatten() + }) + .collect(); + + assert!( + !versions.is_empty(), + "{package_name} should be present in Cargo.lock" + ); + + versions +} + +fn package_version<'a>(lockfile: &'a str, package_name: &str) -> &'a str { + let versions = package_versions(lockfile, package_name); + + assert_eq!( + versions.len(), + 1, + "{package_name} should have one locked version; found {versions:?}" + ); + + versions[0] +} + +fn version_tuple(version: &str) -> (u64, u64, u64) { + let mut parts = version.split('.').map(|part| { + part.parse::() + .unwrap_or_else(|_| panic!("{version} should contain numeric version parts")) + }); + + let major = parts + .next() + .unwrap_or_else(|| panic!("{version} should include a major version")); + let minor = parts + .next() + .unwrap_or_else(|| panic!("{version} should include a minor version")); + let patch = parts + .next() + .unwrap_or_else(|| panic!("{version} should include a patch version")); + + assert!( + parts.next().is_none(), + "{version} should only include major.minor.patch" + ); + + (major, minor, patch) +} + +fn assert_package_at_least(lockfile: &str, package_name: &str, minimum: &str) { + let actual = package_version(lockfile, package_name); + + assert!( + version_tuple(actual) >= version_tuple(minimum), + "{package_name} should stay on at least {minimum}; found {actual}" + ); +} + +#[test] +fn cargo_lock_uses_patched_openssl() { + let lockfile = cargo_lock(); + + assert_package_at_least(&lockfile, "openssl", "0.10.79"); +} + +#[test] +fn cargo_lock_uses_patched_hickory_proto() { + let lockfile = cargo_lock(); + + assert_package_at_least(&lockfile, "hickory-proto", "0.26.1"); +} + +#[test] +fn cargo_lock_uses_patched_lz4_flex() { + let lockfile = cargo_lock(); + let versions = package_versions(&lockfile, "lz4_flex"); + + assert!( + versions.iter().all(|version| { + let parsed = version_tuple(version); + parsed > version_tuple("0.11.5") && parsed != version_tuple("0.12.0") + }), + "lz4_flex should stay outside vulnerable ranges <=0.11.5 and 0.12.0; found {versions:?}" + ); +} \ No newline at end of file