Skip to content
This repository has been archived by the owner on Sep 21, 2024. It is now read-only.

Commit

Permalink
feat: Validate petnames and slugs, disallow an empty strings. (#382)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsantell authored May 12, 2023
1 parent aa1cec7 commit fdda233
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 6 deletions.
13 changes: 12 additions & 1 deletion rust/noosphere-sphere/src/content/write.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{anyhow, Result};
use cid::Cid;
use libipld_cbor::DagCborCodec;
use noosphere_core::data::{BodyChunkIpld, Header, MemoIpld};
Expand All @@ -12,6 +12,14 @@ use async_trait::async_trait;

use crate::{AsyncFileBody, SphereContentRead};

fn validate_slug(slug: &str) -> Result<()> {
if slug.is_empty() {
Err(anyhow!("Slug must not be empty."))
} else {
Ok(())
}
}

/// Anything that can write content to a sphere should implement
/// [SphereContentWrite]. A blanket implementation is provided for anything that
/// implements [HasMutableSphereContext].
Expand Down Expand Up @@ -73,6 +81,7 @@ where
{
async fn link_raw(&mut self, slug: &str, cid: &Cid) -> Result<()> {
self.assert_write_access().await?;
validate_slug(slug)?;

self.sphere_context_mut()
.await?
Expand All @@ -91,6 +100,7 @@ where
additional_headers: Option<Vec<(String, String)>>,
) -> Result<Cid> {
self.assert_write_access().await?;
validate_slug(slug)?;

let memo_cid = {
let current_file = self.read(slug).await?;
Expand Down Expand Up @@ -139,6 +149,7 @@ where
debug!("Writing {}...", slug);

self.assert_write_access().await?;
validate_slug(slug)?;

let mut bytes = Vec::new();
value.read_to_end(&mut bytes).await?;
Expand Down
80 changes: 77 additions & 3 deletions rust/noosphere-sphere/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ pub mod tests {
use wasm_bindgen_test::wasm_bindgen_test;

use crate::{
helpers::{simulated_sphere_context, SimulationAccess},
helpers::{make_valid_link_record, simulated_sphere_context, SimulationAccess},
HasMutableSphereContext, HasSphereContext, SphereContentRead, SphereContentWrite,
SphereContext, SpherePetnameWrite,
};
Expand Down Expand Up @@ -584,8 +584,8 @@ pub mod tests {
async fn it_resolves_none_when_a_petname_is_missing_from_the_sequence() {
initialize_tracing(None);

let name_seqeuence: Vec<String> = vec!["b".into(), "c".into()];
let origin_sphere_context = make_sphere_context_with_peer_chain(&name_seqeuence)
let name_sequence: Vec<String> = vec!["b".into(), "c".into()];
let origin_sphere_context = make_sphere_context_with_peer_chain(&name_sequence)
.await
.unwrap();

Expand All @@ -606,4 +606,78 @@ pub mod tests {

assert!(target_sphere_context.is_none());
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_validates_slug_names_when_writing() -> Result<()> {
initialize_tracing(None);
let valid_names: &[&str] = &["j@__/_大", "/"];
let invalid_names: &[&str] = &[""];

let mut sphere_context =
simulated_sphere_context(SimulationAccess::ReadWrite, None).await?;

for invalid_name in invalid_names {
assert!(sphere_context
.write(
invalid_name,
&ContentType::Text.to_string(),
"hello".as_ref(),
None,
)
.await
.is_err());
}

for valid_name in valid_names {
assert!(sphere_context
.write(
valid_name,
&ContentType::Text.to_string(),
"hello".as_ref(),
None,
)
.await
.is_ok());
}

Ok(())
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_validates_petnames_when_setting() -> Result<()> {
initialize_tracing(None);
let valid_names: &[&str] = &["j@__/_大"];
let invalid_names: &[&str] = &[""];

let mut sphere_context =
simulated_sphere_context(SimulationAccess::ReadWrite, None).await?;
let mut db = sphere_context.sphere_context().await?.db().clone();
let (other_identity, link_record, _) = make_valid_link_record(&mut db).await?;

for invalid_name in invalid_names {
assert!(sphere_context
.adopt_petname(&invalid_name, &link_record)
.await
.is_err());
assert!(sphere_context
.set_petname(&invalid_name, Some(other_identity.clone()))
.await
.is_err());
}

for valid_name in valid_names {
assert!(sphere_context
.adopt_petname(&valid_name, &link_record)
.await
.is_ok());
assert!(sphere_context
.set_petname(&valid_name, Some(other_identity.clone()))
.await
.is_ok());
}

Ok(())
}
}
2 changes: 1 addition & 1 deletion rust/noosphere-sphere/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! async fn main() -> Result<()> {
//! let mut sphere_context = simulated_sphere_context(SimulationAccess::ReadWrite, None).await?;
//!
//! sphere_context.write("/foo", "text/plain", "bar".as_ref(), None).await?;
//! sphere_context.write("foo", "text/plain", "bar".as_ref(), None).await?;
//! sphere_context.save(None).await?;
//!
//! Ok(())
Expand Down
12 changes: 11 additions & 1 deletion rust/noosphere-sphere/src/petname/write.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
use anyhow::Result;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use noosphere_core::data::{Did, IdentityIpld, Jwt};
use noosphere_storage::Storage;
use ucan::{crypto::KeyMaterial, store::UcanJwtStore, Ucan};

use crate::{internal::SphereContextInternal, HasMutableSphereContext, SpherePetnameRead};

fn validate_petname(petname: &str) -> Result<()> {
if petname.is_empty() {
Err(anyhow!("Petname must not be empty."))
} else {
Ok(())
}
}

/// Anything that can write petnames to a sphere should implement
/// [SpherePetnameWrite]. A blanket implementation is provided for anything that
/// implements [HasMutableSphereContext]
Expand Down Expand Up @@ -40,6 +48,7 @@ where
{
async fn set_petname(&mut self, name: &str, identity: Option<Did>) -> Result<()> {
self.assert_write_access().await?;
validate_petname(name)?;

let current_address = self.get_petname(name).await?;

Expand Down Expand Up @@ -69,6 +78,7 @@ where

async fn adopt_petname(&mut self, name: &str, record: &Jwt) -> Result<Option<Did>> {
self.assert_write_access().await?;
validate_petname(name)?;

let ucan = Ucan::try_from(record.as_str())?;
let identity = Did::from(ucan.audience());
Expand Down

0 comments on commit fdda233

Please sign in to comment.