Skip to content

Commit 37a38a4

Browse files
GearsDatapackslpil
authored andcommitted
Replace protobuf with prost
1 parent 295731c commit 37a38a4

File tree

13 files changed

+76
-2058
lines changed

13 files changed

+76
-2058
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Replaced protobuf dependency with prost to avoid a security vulnerability.
6+
37
## v3.2.0 - 2025-03-07
48

59
- Updated protobuf to 3.7.1 and regenerated code from .proto files

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ lazy_static = "1.4"
2626
regex = "1.3"
2727
# Byte collections
2828
bytes = "1"
29-
# Protobuf runtime
30-
protobuf = "=3.7.1"
3129
# gzip (de)compression
3230
flate2 = "1.0"
3331
# RSA signature and SHA256 checksum verification
@@ -40,6 +38,8 @@ pubgrub = "0.2"
4038
http-auth-basic = "0.3"
4139
# base16 encoding
4240
base16 = { version = "0.2", features = ["alloc"] }
41+
# Protobuf runtime
42+
prost = "0.13.5"
4343

4444
[dev-dependencies]
4545
# HTTP client
@@ -52,5 +52,5 @@ tokio = { version = "1", features = ["full"] }
5252
toml = "0.8"
5353

5454
[build-dependencies]
55-
# Protobuf code generation
56-
protobuf-codegen = "=3.7.1"
55+
# Protobuf codegen
56+
prost-build = "0.13.5"

build.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
fn main() {
2-
// protobuf_codegen::Codegen::new()
3-
// .protoc()
4-
// .include("proto")
5-
// .input("proto/signed.proto")
6-
// .input("proto/package.proto")
7-
// .input("proto/versions.proto")
8-
// .out_dir("src/proto")
9-
// .run()
10-
// .expect("Failed to generate protobuf code from .proto files.");
2+
prost_build::compile_protos(
3+
&[
4+
"proto/signed.proto",
5+
"proto/package.proto",
6+
"proto/versions.proto",
7+
],
8+
&["proto/"],
9+
)
10+
.expect("Failed to generate prost code from .proto files.");
1111
}

proto/names.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
syntax = "proto2";
22

3+
package names;
4+
35
message Names {
46
// All packages in the repository
57
repeated Package packages = 1;

proto/package.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
syntax = "proto2";
22

3+
package package;
4+
35
message Package {
46
// All releases of the package
57
repeated Release releases = 1;

proto/signed.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
syntax = "proto2";
22

3+
package signed;
4+
35
message Signed {
46
// Signed contents
57
required bytes payload = 1;

proto/versions.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
syntax = "proto2";
22

3+
package versions;
4+
35
message Versions {
46
// All packages in the repository
57
repeated VersionsPackage packages = 1;

src/lib.rs

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bytes::buf::Buf;
1010
use flate2::read::GzDecoder;
1111
use http::{Method, StatusCode};
1212
use lazy_static::lazy_static;
13-
use protobuf::{Message, MessageField};
13+
use prost::Message;
1414
use regex::Regex;
1515
use ring::digest::{Context, SHA256};
1616
use serde::Deserialize;
@@ -19,7 +19,7 @@ use std::{
1919
collections::HashMap,
2020
convert::{TryFrom, TryInto},
2121
fmt::Display,
22-
io::BufReader,
22+
io::{BufReader, Read},
2323
};
2424
use thiserror::Error;
2525
use version::{Range, Version};
@@ -270,16 +270,19 @@ pub fn get_repository_versions_response(
270270
status => return Err(ApiError::unexpected_response(status, body)),
271271
};
272272

273-
let mut body = GzDecoder::new(body.reader());
274-
let signed = Signed::parse_from_reader(&mut body)?;
273+
let mut decoder = GzDecoder::new(body.reader());
274+
let mut body = Vec::new();
275+
decoder.read_to_end(&mut body)?;
276+
277+
let signed = Signed::decode(body.as_slice())?;
275278

276279
let payload =
277280
verify_payload(signed, public_key).map_err(|_| ApiError::IncorrectPayloadSignature)?;
278281

279-
let versions = Versions::parse_from_bytes(&payload)?
282+
let versions = Versions::decode(payload.as_slice())?
280283
.packages
281284
.into_iter()
282-
.map(|mut n| {
285+
.map(|n| {
283286
let parse_version = |v: &str| {
284287
let err = |_| ApiError::InvalidVersionFormat(v.to_string());
285288
Version::parse(v).map_err(err)
@@ -289,7 +292,7 @@ pub fn get_repository_versions_response(
289292
.iter()
290293
.map(|v| parse_version(v.as_str()))
291294
.collect::<Result<Vec<Version>, ApiError>>()?;
292-
Ok((n.take_name(), versions))
295+
Ok((n.name, versions))
293296
})
294297
.collect::<Result<HashMap<_, _>, ApiError>>()?;
295298

@@ -332,22 +335,25 @@ pub fn get_package_response(
332335
}
333336
};
334337

335-
let mut body = GzDecoder::new(body.reader());
336-
let signed = Signed::parse_from_reader(&mut body)?;
338+
let mut decoder = GzDecoder::new(body.reader());
339+
let mut body = Vec::new();
340+
decoder.read_to_end(&mut body)?;
341+
342+
let signed = Signed::decode(body.as_slice())?;
337343

338344
let payload =
339345
verify_payload(signed, public_key).map_err(|_| ApiError::IncorrectPayloadSignature)?;
340346

341-
let mut package = proto::package::Package::parse_from_bytes(&payload)?;
347+
let package = proto::package::Package::decode(payload.as_slice())?;
342348
let releases = package
343349
.releases
344350
.clone()
345351
.into_iter()
346352
.map(proto_to_release)
347353
.collect::<Result<Vec<_>, _>>()?;
348354
let package = Package {
349-
name: package.take_name(),
350-
repository: package.take_repository(),
355+
name: package.name,
356+
repository: package.repository,
351357
releases,
352358
};
353359

@@ -695,7 +701,7 @@ pub enum ApiError {
695701
IncorrectPayloadSignature,
696702

697703
#[error(transparent)]
698-
InvalidProtobuf(#[from] protobuf::Error),
704+
InvalidProtobuf(#[from] prost::DecodeError),
699705

700706
#[error("unexpected version format {0}")]
701707
InvalidVersionFormat(String),
@@ -762,60 +768,52 @@ fn read_and_check_body(reader: impl std::io::Read, checksum: &[u8]) -> Result<Ve
762768
}
763769

764770
fn proto_to_retirement_status(
765-
status: MessageField<proto::package::RetirementStatus>,
771+
status: Option<proto::package::RetirementStatus>,
766772
) -> Option<RetirementStatus> {
767-
status.into_option().map(|mut stat| RetirementStatus {
768-
message: stat.take_message(),
773+
status.map(|stat| RetirementStatus {
774+
message: stat.message().into(),
769775
reason: proto_to_retirement_reason(stat.reason()),
770776
})
771777
}
772778

773779
fn proto_to_retirement_reason(reason: proto::package::RetirementReason) -> RetirementReason {
774780
use proto::package::RetirementReason::*;
775781
match reason {
776-
RETIRED_OTHER => RetirementReason::Other,
777-
RETIRED_INVALID => RetirementReason::Invalid,
778-
RETIRED_SECURITY => RetirementReason::Security,
779-
RETIRED_DEPRECATED => RetirementReason::Deprecated,
780-
RETIRED_RENAMED => RetirementReason::Renamed,
782+
RetiredOther => RetirementReason::Other,
783+
RetiredInvalid => RetirementReason::Invalid,
784+
RetiredSecurity => RetirementReason::Security,
785+
RetiredDeprecated => RetirementReason::Deprecated,
786+
RetiredRenamed => RetirementReason::Renamed,
781787
}
782788
}
783789

784-
fn proto_to_dep(mut dep: proto::package::Dependency) -> Result<(String, Dependency), ApiError> {
785-
let app = if dep.has_app() {
786-
Some(dep.take_app())
787-
} else {
788-
None
789-
};
790-
let repository = if dep.has_repository() {
791-
Some(dep.take_repository())
792-
} else {
793-
None
794-
};
795-
let requirement = Range::new(dep.take_requirement());
790+
fn proto_to_dep(dep: proto::package::Dependency) -> Result<(String, Dependency), ApiError> {
791+
let app = dep.app;
792+
let repository = dep.repository;
793+
let requirement = Range::new(dep.requirement);
796794
Ok((
797-
dep.take_package(),
795+
dep.package,
798796
Dependency {
799797
requirement,
800-
optional: dep.has_optional(),
798+
optional: dep.optional.is_some(),
801799
app,
802800
repository,
803801
},
804802
))
805803
}
806804

807-
fn proto_to_release(mut release: proto::package::Release) -> Result<Release<()>, ApiError> {
805+
fn proto_to_release(release: proto::package::Release) -> Result<Release<()>, ApiError> {
808806
let dependencies = release
809807
.dependencies
810808
.clone()
811809
.into_iter()
812810
.map(proto_to_dep)
813811
.collect::<Result<HashMap<_, _>, _>>()?;
814-
let version =
815-
Version::try_from(release.version()).expect("Failed to parse version format from Hex");
812+
let version = Version::try_from(release.version.as_str())
813+
.expect("Failed to parse version format from Hex");
816814
Ok(Release {
817815
version,
818-
outer_checksum: release.take_outer_checksum(),
816+
outer_checksum: release.outer_checksum.unwrap_or_default(),
819817
retirement_status: proto_to_retirement_status(release.retired),
820818
requirements: dependencies,
821819
meta: (),
@@ -980,7 +978,7 @@ fn verify_payload(mut signed: Signed, pem_public_key: &[u8]) -> Result<Vec<u8>,
980978
.map_err(|_| ApiError::IncorrectPayloadSignature)?;
981979
let (_, spki) = x509_parser::prelude::SubjectPublicKeyInfo::from_der(&pem.contents)
982980
.map_err(|_| ApiError::IncorrectPayloadSignature)?;
983-
let payload = signed.take_payload();
981+
let payload = std::mem::take(&mut signed.payload);
984982
let verification = ring::signature::UnparsedPublicKey::new(
985983
&ring::signature::RSA_PKCS1_2048_8192_SHA512,
986984
&spki.subject_public_key,

src/proto.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![allow(clippy::enum_variant_names)]
2+
3+
pub mod package {
4+
include!(concat!(env!("OUT_DIR"), "/package.rs"));
5+
}
6+
7+
pub mod signed {
8+
include!(concat!(env!("OUT_DIR"), "/signed.rs"));
9+
}
10+
11+
pub mod versions {
12+
include!(concat!(env!("OUT_DIR"), "/versions.rs"));
13+
}

src/proto/mod.rs

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)