Skip to content

Commit

Permalink
Validate usernames / token ids (#40)
Browse files Browse the repository at this point in the history
* Validate username based on a set of constraints in #39

* Break out some more helpers
  • Loading branch information
the-frey authored Dec 22, 2021
1 parent bac732f commit cc9f4c6
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 7 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ cosmwasm-std = { version = "1.0.0-beta2" }
schemars = "0.8.6"
serde = { version = "1.0.130", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.30" }
regex = "1.5"
regex = "1.5.4"
lazy_static = "1.4.0"


[dev-dependencies]
cosmwasm-schema = { version = "1.0.0-beta2" }
42 changes: 42 additions & 0 deletions src/contract_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
mod tests {
use crate::entry;

use crate::utils::validate_username_characters;

use crate::msg::{
ContractInfoResponse, ExecuteMsg, Extension, InstantiateMsg, Metadata, MintMsg,
PrimaryAliasResponse, QueryMsg, SurchargeInfo, UpdateMetadataMsg, UpdateMintingFeesMsg,
Expand All @@ -16,6 +18,46 @@ mod tests {

use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};

// test some utils first
#[test]
fn username_validator() {
let first_check = validate_username_characters("jeffvader");
assert_eq!(first_check, true);

let second_check = validate_username_characters("jeff-vader");
assert_eq!(second_check, true);

let third_check = validate_username_characters("jeff--vader");
assert_eq!(third_check, false);

let fourth_check = validate_username_characters("_jeff-vader");
assert_eq!(fourth_check, true);

let fifth_check = validate_username_characters("jeff_vader");
assert_eq!(fifth_check, true);

let sixth_check = validate_username_characters("_jeff_vader");
assert_eq!(sixth_check, true);

let seventh_check = validate_username_characters("-jeff_vader");
assert_eq!(seventh_check, true);

let eighth_check = validate_username_characters("__jeffvader");
assert_eq!(eighth_check, false);

let ninth_check = validate_username_characters("j3ffv4d3r");
assert_eq!(ninth_check, true);

let tenth_check = validate_username_characters("j3ff_v4d3r");
assert_eq!(tenth_check, true);

let eleventh_check = validate_username_characters("j3ff__v4d3r");
assert_eq!(eleventh_check, false);

let twelfth_check = validate_username_characters("jeff_-vader");
assert_eq!(twelfth_check, false);
}

const CREATOR: &str = "creator";
const MINTER: &str = "jeff-vader";
const CONTRACT_NAME: &str = "whoami";
Expand Down
13 changes: 7 additions & 6 deletions src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use cw2::set_contract_version;
use cw721::Cw721ReceiveMsg;
use cw721_base::state::TokenInfo;
use cw721_base::ContractError;
use std::convert::TryFrom;

use std::convert::TryInto;

use crate::msg::{
Expand All @@ -12,7 +12,10 @@ use crate::msg::{
};

use crate::state::{CONTRACT_INFO, MINTING_FEES_INFO, PRIMARY_ALIASES};
use crate::utils::{get_mint_fee, get_mint_response, get_number_of_owned_tokens, verify_logo};
use crate::utils::{
get_mint_fee, get_mint_response, get_number_of_owned_tokens, get_username_length,
username_is_valid, verify_logo,
};
use crate::Cw721MetadataContract;

// version info for migration info
Expand Down Expand Up @@ -173,15 +176,13 @@ pub fn mint(
let owner_address = deps.api.addr_validate(&msg.owner)?;

// username == token_id
// validate username length. this, or to some number of bytes?
let username = &msg.token_id;
let username_length = u32::try_from(username.chars().count()).unwrap();
if username_length > 20 {
if !username_is_valid(username) {
return Err(ContractError::Unauthorized {});
}

// work out what fees are owed
let fee = get_mint_fee(minting_fees.clone(), username_length);
let fee = get_mint_fee(minting_fees.clone(), get_username_length(username));

// create the token
// this will fail if token_id (i.e. username)
Expand Down
36 changes: 36 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ use cw20::{EmbeddedLogo, Logo};
use cw721_base::ContractError;

use crate::Cw721MetadataContract;
use lazy_static::lazy_static;
use regex::Regex;
use std::convert::TryFrom;

pub fn get_username_length(username: &str) -> u32 {
u32::try_from(username.chars().count()).unwrap()
}

// validate username length. this, or to some number of bytes?
pub fn validate_username_length(username: &str) -> bool {
let username_length = get_username_length(username);
username_length <= 20
}

pub fn validate_username_characters(username: &str) -> bool {
// first check for allowed characters
lazy_static! {
static ref VALID: Regex = Regex::new(r"[a-z0-9_\-]").unwrap();
}
let first_check_passed = VALID.is_match(username);

// then check for invalid sequence of hyphens or underscores
// if is_match returns true, it is invalid
lazy_static! {
static ref INVALID: Regex = Regex::new(r"[_\-]{2,}").unwrap();
}
let second_check_passed = !INVALID.is_match(username);

first_check_passed && second_check_passed
}

pub fn username_is_valid(username: &str) -> bool {
let username_length_valid = validate_username_length(username);
let username_characters_valid = validate_username_characters(username);
username_characters_valid && username_length_valid
}

pub fn get_mint_fee(minting_fees: MintingFeesResponse, username_length: u32) -> Option<Uint128> {
// is token name short enough to trigger a surcharge?
Expand Down

0 comments on commit cc9f4c6

Please sign in to comment.