Crate name validation on publish
Diff
chartered-web/src/endpoints/cargo_api/publish.rs | 26 ++++++++++++++++++++++++++
chartered-web/src/endpoints/web_api/auth/password.rs | 5 +++++
2 files changed, 30 insertions(+), 1 deletion(-)
@@ -16,6 +16,8 @@
JsonParse(#[from] serde_json::Error),
#[error("Invalid body")]
MetadataParse,
#[error("expected a valid crate name to start with a letter, contain only letters, numbers, hyphens, or underscores and have at most 64 characters ")]
InvalidCrateName,
}
impl Error {
@@ -24,7 +26,9 @@
match self {
Self::Database(e) => e.status_code(),
Self::JsonParse(_) | Self::MetadataParse => StatusCode::BAD_REQUEST,
Self::JsonParse(_) | Self::MetadataParse | Self::InvalidCrateName => {
StatusCode::BAD_REQUEST
}
}
}
}
@@ -43,6 +47,22 @@
other: Vec<String>,
}
fn validate_crate_name(name: &str) -> bool {
const MAX_NAME_LENGTH: usize = 64;
let starts_with_alphabetic = name
.chars()
.next()
.map(|c| c.is_ascii_alphabetic())
.unwrap_or_default();
let is_alphanumeric = name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_');
let is_under_max_length = name.len() < MAX_NAME_LENGTH;
starts_with_alphabetic && is_alphanumeric && is_under_max_length
}
pub async fn handle(
extract::Path((_session_key, organisation)): extract::Path<(String, String)>,
extract::Extension(db): extract::Extension<ConnectionPool>,
@@ -52,6 +72,10 @@
let (_, (metadata_bytes, crate_bytes)) =
parse(body.as_ref()).map_err(|_| Error::MetadataParse)?;
let metadata: Metadata = serde_json::from_slice(metadata_bytes)?;
if !validate_crate_name(&metadata.inner.name) {
return Err(Error::InvalidCrateName);
}
let crate_with_permissions = Crate::find_by_name(
db.clone(),
@@ -30,6 +30,11 @@
user_agent: Option<extract::TypedHeader<headers::UserAgent>>,
addr: extract::ConnectInfo<std::net::SocketAddr>,
) -> Result<Json<super::LoginResponse>, Error> {
if req.username.contains(':') {
return Err(Error::UnknownUser);
}
let user = User::find_by_username(db.clone(), req.username)
.await?