🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-10-09 3:54:19.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-10-09 3:54:19.0 +01:00:00
commit
9065b8577942e3c732e85b1782785cbe87173442 [patch]
tree
f41b3f0abc47fd33dd47a38f8ad9a6651e662fdf
parent
68e287400b489001e76348674c64d152eba7eac1
download
9065b8577942e3c732e85b1782785cbe87173442.tar.gz

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(-)

diff --git a/chartered-web/src/endpoints/cargo_api/publish.rs b/chartered-web/src/endpoints/cargo_api/publish.rs
index 83b4ebd..07f2a81 100644
--- a/chartered-web/src/endpoints/cargo_api/publish.rs
+++ a/chartered-web/src/endpoints/cargo_api/publish.rs
@@ -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(),
diff --git a/chartered-web/src/endpoints/web_api/auth/password.rs b/chartered-web/src/endpoints/web_api/auth/password.rs
index 5e2b720..c523154 100644
--- a/chartered-web/src/endpoints/web_api/auth/password.rs
+++ a/chartered-web/src/endpoints/web_api/auth/password.rs
@@ -30,6 +30,11 @@
    user_agent: Option<extract::TypedHeader<headers::UserAgent>>,
    addr: extract::ConnectInfo<std::net::SocketAddr>,
) -> Result<Json<super::LoginResponse>, Error> {
    // we use `:` as a splitter for openid logins so it isn't legal during password login
    if req.username.contains(':') {
        return Err(Error::UnknownUser);
    }

    // TODO: passwords
    let user = User::find_by_username(db.clone(), req.username)
        .await?