1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-03 04:48:52 -05:00

fix(npm): properly handle legacy shasum of package (#20557)

Closes #20554
This commit is contained in:
David Sherret 2023-09-18 16:40:41 -04:00 committed by GitHub
parent dc1da30927
commit 3de9af4d0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 31 deletions

5
Cargo.lock generated
View file

@ -1024,6 +1024,7 @@ dependencies = [
"fwdansi", "fwdansi",
"glibc_version", "glibc_version",
"glob", "glob",
"hex",
"http", "http",
"hyper 0.14.27", "hyper 0.14.27",
"import_map", "import_map",
@ -1588,9 +1589,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_npm" name = "deno_npm"
version = "0.15.0" version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b266a37deae9bc36785d44e9cc1c52d504940b89e5fea63b65119bef44b128a3" checksum = "7aba69155a585297af4b9ba8d204567bcd51b25af77b5f4c8856b867019093ba"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View file

@ -48,7 +48,7 @@ deno_bench_util = { version = "0.112.0", path = "./bench_util" }
test_util = { path = "./test_util" } test_util = { path = "./test_util" }
deno_lockfile = "0.17.1" deno_lockfile = "0.17.1"
deno_media_type = { version = "0.1.1", features = ["module_specifier"] } deno_media_type = { version = "0.1.1", features = ["module_specifier"] }
deno_npm = "0.15.0" deno_npm = "0.15.1"
deno_semver = "0.5.0" deno_semver = "0.5.0"
# exts # exts

View file

@ -85,6 +85,7 @@ fastwebsockets.workspace = true
flate2.workspace = true flate2.workspace = true
fs3.workspace = true fs3.workspace = true
glob = "0.3.1" glob = "0.3.1"
hex.workspace = true
http.workspace = true http.workspace = true
hyper.workspace = true hyper.workspace = true
import_map = "=0.15.0" import_map = "=0.15.0"

View file

@ -10,6 +10,7 @@ use deno_core::parking_lot::RwLock;
use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageDependencyLockfileInfo;
use deno_lockfile::NpmPackageLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_npm::registry::NpmPackageVersionDistInfoIntegrity;
use deno_npm::registry::NpmRegistryApi; use deno_npm::registry::NpmRegistryApi;
use deno_npm::resolution::NpmPackageVersionResolutionError; use deno_npm::resolution::NpmPackageVersionResolutionError;
use deno_npm::resolution::NpmPackagesPartitioned; use deno_npm::resolution::NpmPackagesPartitioned;
@ -391,6 +392,21 @@ fn populate_lockfile_from_snapshot(
fn npm_package_to_lockfile_info( fn npm_package_to_lockfile_info(
pkg: &NpmResolutionPackage, pkg: &NpmResolutionPackage,
) -> NpmPackageLockfileInfo { ) -> NpmPackageLockfileInfo {
fn integrity_for_lockfile(
integrity: NpmPackageVersionDistInfoIntegrity,
) -> String {
match integrity {
NpmPackageVersionDistInfoIntegrity::Integrity {
algorithm,
base64_hash,
} => format!("{}-{}", algorithm, base64_hash),
NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => {
integrity.to_string()
}
NpmPackageVersionDistInfoIntegrity::LegacySha1Hex(hex) => hex.to_string(),
}
}
let dependencies = pkg let dependencies = pkg
.dependencies .dependencies
.iter() .iter()
@ -403,7 +419,7 @@ fn npm_package_to_lockfile_info(
NpmPackageLockfileInfo { NpmPackageLockfileInfo {
display_id: pkg.id.nv.to_string(), display_id: pkg.id.nv.to_string(),
serialized_id: pkg.id.as_serialized(), serialized_id: pkg.id.as_serialized(),
integrity: pkg.dist.integrity().to_string(), integrity: integrity_for_lockfile(pkg.dist.integrity()),
dependencies, dependencies,
} }
} }

View file

@ -8,6 +8,7 @@ use std::path::PathBuf;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_npm::registry::NpmPackageVersionDistInfo; use deno_npm::registry::NpmPackageVersionDistInfo;
use deno_npm::registry::NpmPackageVersionDistInfoIntegrity;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use tar::Archive; use tar::Archive;
@ -31,12 +32,15 @@ pub fn verify_and_extract_tarball(
fn verify_tarball_integrity( fn verify_tarball_integrity(
package: &PackageNv, package: &PackageNv,
data: &[u8], data: &[u8],
npm_integrity: &str, npm_integrity: &NpmPackageVersionDistInfoIntegrity,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
use ring::digest::Context; use ring::digest::Context;
let (algo, expected_checksum) = match npm_integrity.split_once('-') { let (tarball_checksum, expected_checksum) = match npm_integrity {
Some((hash_kind, checksum)) => { NpmPackageVersionDistInfoIntegrity::Integrity {
let algo = match hash_kind { algorithm,
base64_hash,
} => {
let algo = match *algorithm {
"sha512" => &ring::digest::SHA512, "sha512" => &ring::digest::SHA512,
"sha1" => &ring::digest::SHA1_FOR_LEGACY_USE_ONLY, "sha1" => &ring::digest::SHA1_FOR_LEGACY_USE_ONLY,
hash_kind => bail!( hash_kind => bail!(
@ -45,19 +49,28 @@ fn verify_tarball_integrity(
hash_kind hash_kind
), ),
}; };
(algo, checksum.to_lowercase()) let mut hash_ctx = Context::new(algo);
hash_ctx.update(data);
let digest = hash_ctx.finish();
let tarball_checksum = base64::encode(digest.as_ref()).to_lowercase();
(tarball_checksum, base64_hash.to_lowercase())
}
NpmPackageVersionDistInfoIntegrity::LegacySha1Hex(hex) => {
let mut hash_ctx = Context::new(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY);
hash_ctx.update(data);
let digest = hash_ctx.finish();
let tarball_checksum = hex::encode(digest.as_ref()).to_lowercase();
(tarball_checksum, hex.to_lowercase())
}
NpmPackageVersionDistInfoIntegrity::UnknownIntegrity(integrity) => {
bail!(
"Not implemented integrity kind for {}: {}",
package,
integrity
)
} }
None => bail!(
"Not implemented integrity kind for {}: {}",
package,
npm_integrity
),
}; };
let mut hash_ctx = Context::new(algo);
hash_ctx.update(data);
let digest = hash_ctx.finish();
let tarball_checksum = base64::encode(digest.as_ref()).to_lowercase();
if tarball_checksum != expected_checksum { if tarball_checksum != expected_checksum {
bail!( bail!(
"Tarball checksum did not match what was provided by npm registry for {}.\n\nExpected: {}\nActual: {}", "Tarball checksum did not match what was provided by npm registry for {}.\n\nExpected: {}\nActual: {}",
@ -147,36 +160,81 @@ mod test {
let actual_checksum = let actual_checksum =
"z4phnx7vul3xvchq1m2ab9yg5aulvxxcg/spidns6c5h0ne8xyxysp+dgnkhfuwvy7kxvudbeoglodj6+sfapg=="; "z4phnx7vul3xvchq1m2ab9yg5aulvxxcg/spidns6c5h0ne8xyxysp+dgnkhfuwvy7kxvudbeoglodj6+sfapg==";
assert_eq!( assert_eq!(
verify_tarball_integrity(&package, &Vec::new(), "test") verify_tarball_integrity(
.unwrap_err() &package,
.to_string(), &Vec::new(),
&NpmPackageVersionDistInfoIntegrity::UnknownIntegrity("test")
)
.unwrap_err()
.to_string(),
"Not implemented integrity kind for package@1.0.0: test", "Not implemented integrity kind for package@1.0.0: test",
); );
assert_eq!( assert_eq!(
verify_tarball_integrity(&package, &Vec::new(), "notimplemented-test") verify_tarball_integrity(
.unwrap_err() &package,
.to_string(), &Vec::new(),
&NpmPackageVersionDistInfoIntegrity::Integrity {
algorithm: "notimplemented",
base64_hash: "test"
}
)
.unwrap_err()
.to_string(),
"Not implemented hash function for package@1.0.0: notimplemented", "Not implemented hash function for package@1.0.0: notimplemented",
); );
assert_eq!( assert_eq!(
verify_tarball_integrity(&package, &Vec::new(), "sha1-test") verify_tarball_integrity(
.unwrap_err() &package,
.to_string(), &Vec::new(),
&NpmPackageVersionDistInfoIntegrity::Integrity {
algorithm: "sha1",
base64_hash: "test"
}
)
.unwrap_err()
.to_string(),
concat!( concat!(
"Tarball checksum did not match what was provided by npm ", "Tarball checksum did not match what was provided by npm ",
"registry for package@1.0.0.\n\nExpected: test\nActual: 2jmj7l5rsw0yvb/vlwaykk/ybwk=", "registry for package@1.0.0.\n\nExpected: test\nActual: 2jmj7l5rsw0yvb/vlwaykk/ybwk=",
), ),
); );
assert_eq!( assert_eq!(
verify_tarball_integrity(&package, &Vec::new(), "sha512-test") verify_tarball_integrity(
.unwrap_err() &package,
.to_string(), &Vec::new(),
&NpmPackageVersionDistInfoIntegrity::Integrity {
algorithm: "sha512",
base64_hash: "test"
}
)
.unwrap_err()
.to_string(),
format!("Tarball checksum did not match what was provided by npm registry for package@1.0.0.\n\nExpected: test\nActual: {actual_checksum}"), format!("Tarball checksum did not match what was provided by npm registry for package@1.0.0.\n\nExpected: test\nActual: {actual_checksum}"),
); );
assert!(verify_tarball_integrity( assert!(verify_tarball_integrity(
&package, &package,
&Vec::new(), &Vec::new(),
&format!("sha512-{actual_checksum}") &NpmPackageVersionDistInfoIntegrity::Integrity {
algorithm: "sha512",
base64_hash: actual_checksum,
},
)
.is_ok());
let actual_hex = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
assert_eq!(
verify_tarball_integrity(
&package,
&Vec::new(),
&NpmPackageVersionDistInfoIntegrity::LegacySha1Hex("test"),
)
.unwrap_err()
.to_string(),
format!("Tarball checksum did not match what was provided by npm registry for package@1.0.0.\n\nExpected: test\nActual: {actual_hex}"),
);
assert!(verify_tarball_integrity(
&package,
&Vec::new(),
&NpmPackageVersionDistInfoIntegrity::LegacySha1Hex(actual_hex),
) )
.is_ok()); .is_ok());
} }