Skip to content

Commit 38c1188

Browse files
committed
wip: hexpm: support advisories
1 parent 94dd3b0 commit 38c1188

5 files changed

Lines changed: 160 additions & 9 deletions

File tree

compiler-cli/src/dependencies.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,12 +827,14 @@ impl ProvidedPackage {
827827
requirements,
828828
retirement_status: None,
829829
outer_checksum: vec![],
830+
security_advisories: vec![],
830831
meta: (),
831832
};
832833
hexpm::Package {
833834
name: name.as_str().into(),
834835
repository: "local".into(),
835836
releases: vec![release],
837+
advisories: vec![],
836838
}
837839
}
838840

compiler-cli/src/dependencies/tests.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,10 +641,12 @@ fn provided_local_to_hex() {
641641
let hex_package = hexpm::Package {
642642
name: "package".into(),
643643
repository: "local".into(),
644+
advisories: vec![],
644645
releases: vec![hexpm::Release {
645646
version: Version::new(1, 0, 0),
646647
retirement_status: None,
647648
outer_checksum: vec![],
649+
security_advisories: vec![],
648650
meta: (),
649651
requirements: [
650652
(
@@ -700,11 +702,13 @@ fn provided_git_to_hex() {
700702
let hex_package = hexpm::Package {
701703
name: "package".into(),
702704
repository: "local".into(),
705+
advisories: vec![],
703706
releases: vec![hexpm::Release {
704707
version: Version::new(1, 0, 0),
705708
retirement_status: None,
706709
outer_checksum: vec![],
707710
meta: (),
711+
security_advisories: vec![],
708712
requirements: [
709713
(
710714
"req_1".into(),

compiler-core/src/dependency.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ where
4444
let root = hexpm::Package {
4545
name: root_name.as_str().into(),
4646
repository: "local".into(),
47+
advisories: vec![],
4748
releases: vec![Release {
4849
version: root_version.clone(),
4950
outer_checksum: vec![],
5051
retirement_status: None,
5152
requirements,
53+
security_advisories: vec![],
5254
meta: (),
5355
}],
5456
};
@@ -1170,6 +1172,7 @@ but it is locked to 0.2.0, which is incompatible."
11701172
requirements: all_requirements,
11711173
retirement_status: None,
11721174
outer_checksum: vec![1, 2, 3],
1175+
security_advisories: vec![],
11731176
meta: (),
11741177
}
11751178
}
@@ -1182,6 +1185,7 @@ but it is locked to 0.2.0, which is incompatible."
11821185
Rc::new(hexpm::Package {
11831186
name: package.into(),
11841187
repository: "hexpm".into(),
1188+
advisories: vec![],
11851189
releases,
11861190
}),
11871191
);

hexpm/src/lib.rs

Lines changed: 137 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -393,15 +393,25 @@ pub fn repository_v2_package_parse_body(
393393
verify_payload(signed, public_key).map_err(|_| ApiError::IncorrectPayloadSignature)?;
394394

395395
let package = proto::package::Package::decode(payload.as_slice())?;
396-
let releases = package
397-
.releases
398-
.clone()
396+
let proto::package::Package {
397+
name,
398+
repository,
399+
advisories: proto_advisories,
400+
releases: proto_releases,
401+
} = package;
402+
403+
let advisories = proto_advisories
404+
.into_iter()
405+
.map(proto_to_security_advisory)
406+
.collect::<Result<Vec<_>, _>>()?;
407+
let releases = proto_releases
399408
.into_iter()
400-
.map(proto_to_release)
409+
.map(|release| proto_to_release(release, &advisories))
401410
.collect::<Result<Vec<_>, _>>()?;
402411
let package = Package {
403-
name: package.name,
404-
repository: package.repository,
412+
name,
413+
repository,
414+
advisories,
405415
releases,
406416
};
407417

@@ -866,7 +876,10 @@ fn proto_to_dep(dep: proto::package::Dependency) -> Result<(String, Dependency),
866876
))
867877
}
868878

869-
fn proto_to_release(release: proto::package::Release) -> Result<Release<()>, ApiError> {
879+
fn proto_to_release(
880+
release: proto::package::Release,
881+
package_advisories: &[SecurityAdvisory],
882+
) -> Result<Release<()>, ApiError> {
870883
let dependencies = release
871884
.dependencies
872885
.clone()
@@ -875,23 +888,79 @@ fn proto_to_release(release: proto::package::Release) -> Result<Release<()>, Api
875888
.collect::<Result<HashMap<_, _>, _>>()?;
876889
let version = Version::try_from(release.version.as_str())
877890
.expect("Failed to parse version format from Hex");
891+
let security_advisories = package_advisories
892+
.iter()
893+
.enumerate()
894+
.filter_map(|(index, advisory)| {
895+
if release.advisory_indexes.contains(&(index as u32)) {
896+
Some(advisory.clone())
897+
} else {
898+
None
899+
}
900+
})
901+
.collect();
878902
Ok(Release {
879903
version,
880904
outer_checksum: release.outer_checksum.unwrap_or_default(),
881905
retirement_status: proto_to_retirement_status(release.retired),
882906
requirements: dependencies,
907+
security_advisories,
883908
meta: (),
884909
})
885910
}
886911

887-
#[derive(Debug, PartialEq, Eq, Clone)]
912+
fn proto_to_security_advisory(
913+
advisory: proto::package::SecurityAdvisory,
914+
) -> Result<SecurityAdvisory, ApiError> {
915+
let proto::package::SecurityAdvisory {
916+
id,
917+
summary,
918+
aliases,
919+
api_url,
920+
cvss_score,
921+
html_url,
922+
severity,
923+
} = advisory;
924+
// We use this instead of just `SecurityAdvisory::severity()` to save `None`
925+
// state, since otherwise it will be lost.
926+
let severity = severity
927+
.and_then(|severity| severity.try_into().ok())
928+
.map(proto_to_advisory_severity);
929+
Ok(SecurityAdvisory {
930+
id,
931+
summary,
932+
html_url,
933+
severity,
934+
cvss_score,
935+
api_url,
936+
aliases,
937+
})
938+
}
939+
940+
fn proto_to_advisory_severity(severity: proto::package::AdvisorySeverity) -> AdvisorySeverity {
941+
use proto::package::AdvisorySeverity::*;
942+
match severity {
943+
SeverityNone => AdvisorySeverity::None,
944+
SeverityLow => AdvisorySeverity::Low,
945+
SeverityMedium => AdvisorySeverity::Medium,
946+
SeverityHigh => AdvisorySeverity::High,
947+
SeverityCritical => AdvisorySeverity::Critical,
948+
}
949+
}
950+
951+
#[derive(Debug, PartialEq, Clone)]
888952
pub struct Package {
953+
/// Name of package
889954
pub name: String,
955+
/// Name of repository
890956
pub repository: String,
957+
/// All releases of the package
891958
pub releases: Vec<Release<()>>,
959+
/// All security advisories affecting any release of the package
960+
pub advisories: Vec<SecurityAdvisory>,
892961
}
893962

894-
#[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize)]
963+
#[derive(Debug, PartialEq, Clone, serde::Deserialize)]
895964
pub struct Release<Meta> {
896965
/// Release version
897966
pub version: Version,
@@ -904,6 +973,8 @@ pub struct Release<Meta> {
904973
/// required when encoding but optional when decoding
905974
#[serde(alias = "checksum", deserialize_with = "deserialize_checksum")]
906975
pub outer_checksum: Vec<u8>,
976+
/// Security advisories affecting this release of the package
977+
pub security_advisories: Vec<SecurityAdvisory>,
907978
/// This is not present in all API endpoints so may be absent sometimes.
908979
pub meta: Meta,
909980
}
@@ -972,6 +1043,63 @@ impl RetirementReason {
9721043
}
9731044
}
9741045

1046+
#[derive(Debug, PartialEq, Clone, serde::Deserialize)]
1047+
pub struct SecurityAdvisory {
1048+
/// Advisory identifier (e.g. GHSA-xxxx-xxxx-xxxx or CVE-xxxx-xxxxx)
1049+
pub id: String,
1050+
/// Short description of the advisory
1051+
pub summary: String,
1052+
/// OSV web URL for the advisory
1053+
pub html_url: String,
1054+
/// Severity of the advisory
1055+
pub severity: Option<AdvisorySeverity>,
1056+
/// CVSS score (0.0–10.0)
1057+
pub cvss_score: Option<f32>,
1058+
/// OSV API URL for the advisory
1059+
pub api_url: String,
1060+
/// Other identifiers for the same vulnerability (e.g. a CVE id when the
1061+
/// primary id is a GHSA id, or vice versa).
1062+
pub aliases: Vec<String>,
1063+
}
1064+
1065+
#[derive(Debug, PartialEq, Eq, Clone)]
1066+
pub enum AdvisorySeverity {
1067+
None,
1068+
Low,
1069+
Medium,
1070+
High,
1071+
Critical,
1072+
}
1073+
1074+
impl<'de> serde::Deserialize<'de> for AdvisorySeverity {
1075+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1076+
where
1077+
D: serde::Deserializer<'de>,
1078+
{
1079+
let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
1080+
match s {
1081+
"none" => Ok(AdvisorySeverity::None),
1082+
"low" => Ok(AdvisorySeverity::Low),
1083+
"medium" => Ok(AdvisorySeverity::Medium),
1084+
"high" => Ok(AdvisorySeverity::High),
1085+
"critical" => Ok(AdvisorySeverity::Critical),
1086+
_ => Err(serde::de::Error::custom("unknown advisory severity type")),
1087+
}
1088+
}
1089+
}
1090+
1091+
impl AdvisorySeverity {
1092+
pub fn to_str(&self) -> &'static str {
1093+
match self {
1094+
AdvisorySeverity::None => "none",
1095+
AdvisorySeverity::Low => "low",
1096+
AdvisorySeverity::Medium => "medium",
1097+
AdvisorySeverity::High => "high",
1098+
AdvisorySeverity::Critical => "critical",
1099+
}
1100+
}
1101+
}
1102+
9751103
#[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize)]
9761104
pub struct Dependency {
9771105
/// Version requirement of dependency

hexpm/src/tests.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ fn expected_package_exfmt() -> Package {
450450
Package {
451451
name: "exfmt".to_string(),
452452
repository: "hexpm".to_string(),
453+
advisories: vec![],
453454
releases: vec![
454455
Release {
455456
version: Version::try_from("0.0.0").unwrap(),
@@ -459,6 +460,7 @@ fn expected_package_exfmt() -> Package {
459460
82, 48, 191, 145, 92, 172, 0, 108, 238, 71, 57, 23, 101, 177, 161, 83, 91, 182,
460461
18, 232, 249, 225, 29, 12, 246, 5, 215, 165, 32, 57, 179, 110,
461462
],
463+
security_advisories: vec![],
462464
meta: (),
463465
},
464466
Release {
@@ -469,6 +471,7 @@ fn expected_package_exfmt() -> Package {
469471
111, 246, 240, 176, 118, 229, 12, 15, 164, 61, 186, 3, 89, 106, 153, 225, 247,
470472
52, 245, 8, 216, 139, 21, 232, 200, 16, 214, 59, 241, 188, 9, 6,
471473
],
474+
security_advisories: vec![],
472475
meta: (),
473476
},
474477
Release {
@@ -479,6 +482,7 @@ fn expected_package_exfmt() -> Package {
479482
149, 9, 192, 229, 84, 162, 110, 207, 161, 43, 31, 0, 126, 168, 14, 243, 31, 43,
480483
195, 238, 100, 91, 78, 100, 213, 181, 101, 154, 106, 168, 170, 107,
481484
],
485+
security_advisories: vec![],
482486
meta: (),
483487
},
484488
Release {
@@ -489,6 +493,7 @@ fn expected_package_exfmt() -> Package {
489493
157, 229, 28, 212, 92, 249, 14, 240, 235, 104, 31, 12, 160, 199, 83, 195, 154,
490494
105, 222, 37, 221, 80, 181, 183, 113, 240, 234, 107, 144, 85, 255, 65,
491495
],
496+
security_advisories: vec![],
492497
meta: (),
493498
},
494499
Release {
@@ -499,6 +504,7 @@ fn expected_package_exfmt() -> Package {
499504
112, 250, 133, 189, 183, 192, 54, 218, 115, 55, 216, 97, 204, 201, 191, 168,
500505
250, 133, 138, 252, 202, 240, 74, 197, 228, 235, 81, 18, 241, 7, 155, 38,
501506
],
507+
security_advisories: vec![],
502508
meta: (),
503509
},
504510
Release {
@@ -509,6 +515,7 @@ fn expected_package_exfmt() -> Package {
509515
131, 20, 29, 160, 171, 124, 7, 125, 210, 88, 17, 189, 199, 49, 191, 190, 14,
510516
162, 38, 247, 52, 176, 189, 17, 7, 188, 151, 152, 24, 64, 170, 29,
511517
],
518+
security_advisories: vec![],
512519
meta: (),
513520
},
514521
Release {
@@ -519,6 +526,7 @@ fn expected_package_exfmt() -> Package {
519526
109, 162, 185, 169, 26, 4, 62, 60, 167, 54, 182, 161, 140, 197, 75, 113, 183,
520527
117, 247, 201, 218, 228, 14, 160, 115, 157, 196, 51, 108, 16, 96, 217,
521528
],
529+
security_advisories: vec![],
522530
meta: (),
523531
},
524532
Release {
@@ -529,6 +537,7 @@ fn expected_package_exfmt() -> Package {
529537
97, 50, 95, 212, 242, 59, 245, 177, 140, 78, 79, 180, 108, 174, 119, 176, 24,
530538
80, 218, 152, 178, 227, 152, 242, 32, 126, 72, 67, 222, 0, 173, 170,
531539
],
540+
security_advisories: vec![],
532541
meta: (),
533542
},
534543
Release {
@@ -539,6 +548,7 @@ fn expected_package_exfmt() -> Package {
539548
246, 178, 237, 214, 217, 158, 143, 52, 130, 186, 64, 50, 94, 175, 161, 81, 68,
540549
186, 4, 73, 53, 226, 235, 144, 209, 84, 231, 136, 165, 119, 122, 126,
541550
],
551+
security_advisories: vec![],
542552
meta: (),
543553
},
544554
Release {
@@ -549,6 +559,7 @@ fn expected_package_exfmt() -> Package {
549559
151, 86, 157, 218, 218, 131, 240, 119, 198, 216, 202, 240, 65, 17, 57, 228, 84,
550560
252, 59, 207, 246, 49, 22, 21, 52, 47, 51, 139, 190, 9, 95, 109,
551561
],
562+
security_advisories: vec![],
552563
meta: (),
553564
},
554565
],
@@ -891,6 +902,7 @@ fn get_package_release_response_ok() {
891902
"app": "cowboy"
892903
}
893904
},
905+
"security_advisories": [],
894906
"meta": {
895907
"app": "clint",
896908
"build_tools": ["mix"]
@@ -903,6 +915,7 @@ fn get_package_release_response_ok() {
903915
resp,
904916
Release {
905917
version: Version::new(0, 0, 1),
918+
security_advisories: vec![],
906919
requirements: [
907920
(
908921
"plug".into(),

0 commit comments

Comments
 (0)