Skip to content

Commit 963d83b

Browse files
authored
publish: Move crate name and version validation to publish controller (#7237)
1 parent 0ad1d9d commit 963d83b

9 files changed

+37
-55
lines changed

src/controllers/krate/publish.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,31 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
5555
let metadata: PublishMetadata = serde_json::from_slice(&json_bytes)
5656
.map_err(|e| cargo_err(&format_args!("invalid upload request: {e}")))?;
5757

58+
if !Crate::valid_name(&metadata.name) {
59+
return Err(cargo_err(&format_args!(
60+
"\"{}\" is an invalid crate name (crate names must start with a \
61+
letter, contain only letters, numbers, hyphens, or underscores and \
62+
have at most {MAX_NAME_LENGTH} characters)",
63+
metadata.name
64+
)));
65+
}
66+
67+
let version = match semver::Version::parse(&metadata.vers) {
68+
Ok(parsed) => parsed,
69+
Err(_) => {
70+
return Err(cargo_err(&format_args!(
71+
"\"{}\" is an invalid semver version",
72+
metadata.vers
73+
)))
74+
}
75+
};
76+
77+
// Convert the version back to a string to deal with any inconsistencies
78+
let version_string = version.to_string();
79+
5880
let request_log = req.request_log();
5981
request_log.add("crate_name", &*metadata.name);
60-
request_log.add("crate_version", &*metadata.vers);
82+
request_log.add("crate_version", &version_string);
6183

6284
conduit_compat(move || {
6385
let conn = &mut *app.db_write()?;
@@ -114,7 +136,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
114136
)));
115137
}
116138

117-
let pkg_name = format!("{}-{}", &*metadata.name, &*metadata.vers);
139+
let pkg_name = format!("{}-{}", &*metadata.name, &version_string);
118140
let tarball_info = process_tarball(&pkg_name, &*tarball_bytes, maximums.max_unpack_size)?;
119141

120142
// `unwrap()` is safe here since `process_tarball()` validates that
@@ -208,7 +230,6 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
208230
// commit the transactions to record a new or updated crate.
209231
conn.transaction(|conn| {
210232
let name = metadata.name;
211-
let vers = &*metadata.vers;
212233
let keywords = keywords.iter().map(|s| s.as_str()).collect::<Vec<_>>();
213234
let categories = categories.iter().map(|s| s.as_str()).collect::<Vec<_>>();
214235

@@ -263,7 +284,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
263284
// Persist the new version of this crate
264285
let version = NewVersion::new(
265286
krate.id,
266-
vers,
287+
&version,
267288
&features,
268289
license,
269290
// Downcast is okay because the file length must be less than the max upload size
@@ -321,7 +342,7 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
321342
Handle::current()
322343
.block_on(app.storage.upload_crate_file(
323344
&krate.name,
324-
&vers.to_string(),
345+
&version_string,
325346
tarball_bytes,
326347
))
327348
.map_err(|e| internal(format!("failed to upload crate: {e}")))?;

src/tests/builders/publish.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ impl PublishBuilder {
134134

135135
pub fn build(self) -> (String, Vec<u8>) {
136136
let metadata = u::PublishMetadata {
137-
name: u::EncodableCrateName(self.krate_name.clone()),
138-
vers: u::EncodableCrateVersion(self.version.clone()),
137+
name: self.krate_name.clone(),
138+
vers: self.version.to_string(),
139139
deps: self.deps.clone(),
140140
readme: self.readme,
141141
readme_file: None,

src/tests/krate/publish/snapshots/all__krate__publish__validation__invalid_names-2.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "invalid upload request: invalid value: string \"foo bar\", expected a valid crate name to start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters at line 1 column 17"
8+
"detail": "\"foo bar\" is an invalid crate name (crate names must start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__validation__invalid_names-3.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "invalid upload request: invalid value: string \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", expected a valid crate name to start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters at line 1 column 75"
8+
"detail": "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" is an invalid crate name (crate names must start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__validation__invalid_names-4.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "invalid upload request: invalid value: string \"snow☃\", expected a valid crate name to start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters at line 1 column 17"
8+
"detail": "\"snow☃\" is an invalid crate name (crate names must start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__validation__invalid_names-5.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "invalid upload request: invalid value: string \"áccênts\", expected a valid crate name to start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters at line 1 column 19"
8+
"detail": "\"áccênts\" is an invalid crate name (crate names must start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__validation__invalid_names.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "invalid upload request: invalid value: string \"\", expected a valid crate name to start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters at line 1 column 10"
8+
"detail": "\"\" is an invalid crate name (crate names must start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters)"
99
}
1010
]
1111
}

src/tests/krate/publish/snapshots/all__krate__publish__validation__invalid_version.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ expression: response.into_json()
55
{
66
"errors": [
77
{
8-
"detail": "invalid upload request: invalid value: string \"broken\", expected a valid semver at line 1 column 29"
8+
"detail": "\"broken\" is an invalid semver version"
99
}
1010
]
1111
}

src/views/krate_publish.rs

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@
33
//! to and from structs. The serializing is only utilised in
44
//! integration tests.
55
6-
use serde::{de, Deserialize, Deserializer, Serialize};
6+
use serde::{Deserialize, Serialize};
77

8-
use crate::models::krate::MAX_NAME_LENGTH;
9-
10-
use crate::models::Crate;
118
use crate::models::DependencyKind;
129

1310
#[derive(Deserialize, Serialize, Debug)]
1411
pub struct PublishMetadata {
15-
pub name: EncodableCrateName,
16-
pub vers: EncodableCrateVersion,
12+
pub name: String,
13+
pub vers: String,
1714
pub deps: Vec<EncodableCrateDependency>,
1815
pub readme: Option<String>,
1916
pub readme_file: Option<String>,
@@ -31,39 +28,3 @@ pub struct EncodableCrateDependency {
3128
pub explicit_name_in_toml: Option<String>,
3229
pub registry: Option<String>,
3330
}
34-
35-
#[derive(PartialEq, Eq, Hash, Serialize, Clone, Debug, Deref)]
36-
pub struct EncodableCrateName(pub String);
37-
38-
impl<'de> Deserialize<'de> for EncodableCrateName {
39-
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<EncodableCrateName, D::Error> {
40-
let s = String::deserialize(d)?;
41-
if !Crate::valid_name(&s) {
42-
let value = de::Unexpected::Str(&s);
43-
let expected = format!(
44-
"a valid crate name to start with a letter, contain only letters, \
45-
numbers, hyphens, or underscores and have at most {MAX_NAME_LENGTH} characters"
46-
);
47-
Err(de::Error::invalid_value(value, &expected.as_ref()))
48-
} else {
49-
Ok(EncodableCrateName(s))
50-
}
51-
}
52-
}
53-
54-
#[derive(Serialize, Debug, Deref)]
55-
pub struct EncodableCrateVersion(pub semver::Version);
56-
57-
impl<'de> Deserialize<'de> for EncodableCrateVersion {
58-
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<EncodableCrateVersion, D::Error> {
59-
let s = String::deserialize(d)?;
60-
match semver::Version::parse(&s) {
61-
Ok(v) => Ok(EncodableCrateVersion(v)),
62-
Err(..) => {
63-
let value = de::Unexpected::Str(&s);
64-
let expected = "a valid semver";
65-
Err(de::Error::invalid_value(value, &expected))
66-
}
67-
}
68-
}
69-
}

0 commit comments

Comments
 (0)