Adds safety check for valid package names

This commit is contained in:
howardwu 2021-02-24 19:25:41 -08:00
parent c61cd3459a
commit 73b550011e
14 changed files with 139 additions and 41 deletions

View File

@ -40,13 +40,20 @@ impl Command for Init {
}
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
let path = current_dir()?;
// Derive the package directory path.
let mut path = current_dir()?;
// Check that the given package name is valid.
let package_name = path
.file_stem()
.ok_or_else(|| anyhow!("Project name invalid"))?
.to_string_lossy()
.to_string();
if !LeoPackage::is_package_name_valid(&package_name) {
return Err(anyhow!("Invalid Leo project name"));
}
// Check that the current package directory path exists.
if !path.exists() {
return Err(anyhow!("Directory does not exist"));
}

View File

@ -43,13 +43,17 @@ impl Command for New {
}
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
let mut path = current_dir()?;
// Check that the given package name is valid.
let package_name = self.name;
if !LeoPackage::is_package_name_valid(&package_name) {
return Err(anyhow!("Invalid Leo project name"));
}
// Derive the package directory path
// Derive the package directory path.
let mut path = current_dir()?;
path.push(&package_name);
// Verify the package directory path does not exist yet
// Verify the package directory path does not exist yet.
if path.exists() {
return Err(anyhow!("Directory already exists {:?}", path));
}

View File

@ -32,7 +32,7 @@ use tracing::span::Span;
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Prove {
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
pub(super) skip_key_check: bool,
pub(crate) skip_key_check: bool,
}
impl Command for Prove {

View File

@ -30,7 +30,7 @@ use tracing::span::Span;
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Run {
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
skip_key_check: bool,
pub(crate) skip_key_check: bool,
}
impl Command for Run {

View File

@ -35,7 +35,7 @@ use tracing::span::Span;
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Setup {
#[structopt(long = "skip-key-check", help = "Skip key verification")]
pub(super) skip_key_check: bool,
pub(crate) skip_key_check: bool,
}
impl Command for Setup {

View File

@ -36,7 +36,7 @@ use tracing::span::Span;
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct Test {
#[structopt(short = "f", long = "file", name = "file")]
files: Vec<PathBuf>,
pub(crate) files: Vec<PathBuf>,
}
impl Command for Test {

View File

@ -35,15 +35,15 @@ pub enum Automatic {
pub struct Update {
/// List all available versions of Leo
#[structopt(short, long)]
list: bool,
pub(crate) list: bool,
/// For Aleo Studio only
#[structopt(short, long)]
studio: bool,
pub(crate) studio: bool,
/// Setting for automatic updates of Leo
#[structopt(subcommand)]
automatic: Option<Automatic>,
pub(crate) automatic: Option<Automatic>,
}
impl Command for Update {

View File

@ -39,34 +39,34 @@ const PEDERSEN_HASH_PATH: &str = "./examples/pedersen-hash/";
#[test]
pub fn build_pedersen_hash() -> Result<()> {
Build::new().apply(context()?, ())?;
(Build {}).apply(context()?, ())?;
Ok(())
}
#[test]
pub fn setup_pedersen_hash() -> Result<()> {
let build = Build::new().apply(context()?, ())?;
Setup::new(false).apply(context()?, build.clone())?;
Setup::new(true).apply(context()?, build)?;
let build = (Build {}).apply(context()?, ())?;
(Setup { skip_key_check: false }).apply(context()?, build.clone())?;
(Setup { skip_key_check: true }).apply(context()?, build)?;
Ok(())
}
#[test]
pub fn prove_pedersen_hash() -> Result<()> {
let build = Build::new().apply(context()?, ())?;
let setup = Setup::new(false).apply(context()?, build)?;
Prove::new(false).apply(context()?, setup.clone())?;
Prove::new(true).apply(context()?, setup)?;
let build = (Build {}).apply(context()?, ())?;
let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
(Prove { skip_key_check: false }).apply(context()?, setup.clone())?;
(Prove { skip_key_check: true }).apply(context()?, setup)?;
Ok(())
}
#[test]
pub fn run_pedersen_hash() -> Result<()> {
let build = Build::new().apply(context()?, ())?;
let setup = Setup::new(false).apply(context()?, build)?;
let prove = Prove::new(false).apply(context()?, setup)?;
Run::new(false).apply(context()?, prove.clone())?;
Run::new(true).apply(context()?, prove)?;
let build = (Build {}).apply(context()?, ())?;
let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
let prove = (Prove { skip_key_check: false }).apply(context()?, setup)?;
(Run { skip_key_check: false }).apply(context()?, prove.clone())?;
(Run { skip_key_check: true }).apply(context()?, prove)?;
Ok(())
}
@ -75,14 +75,14 @@ pub fn test_pedersen_hash() -> Result<()> {
let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH);
main_file.push("src/main.leo");
Test::new(Vec::new()).apply(context()?, ())?;
Test::new(vec![main_file]).apply(context()?, ())?;
(Test { files: vec![] }).apply(context()?, ())?;
(Test { files: vec![main_file] }).apply(context()?, ())?;
Ok(())
}
#[test]
pub fn test_logout() -> Result<()> {
Logout::new().apply(context()?, ())?;
(Logout {}).apply(context()?, ())?;
Ok(())
}
@ -111,12 +111,40 @@ pub fn login_incorrect_credentials_or_token() -> Result<()> {
#[test]
pub fn leo_update_and_update_automatic() -> Result<()> {
Update::new(true, true, None).apply(context()?, ())?;
Update::new(false, true, None).apply(context()?, ())?;
Update::new(false, false, None).apply(context()?, ())?;
let update = Update {
list: true,
studio: true,
automatic: None,
};
update.apply(context()?, ())?;
Update::new(false, false, Some(UpdateAutomatic::Automatic { value: true })).apply(context()?, ())?;
Update::new(false, false, Some(UpdateAutomatic::Automatic { value: false })).apply(context()?, ())?;
let update = Update {
list: false,
studio: true,
automatic: None,
};
update.apply(context()?, ())?;
let update = Update {
list: false,
studio: false,
automatic: None,
};
update.apply(context()?, ())?;
let update = Update {
list: false,
studio: false,
automatic: Some(UpdateAutomatic::Automatic { value: true }),
};
update.apply(context()?, ())?;
let update = Update {
list: false,
studio: false,
automatic: Some(UpdateAutomatic::Automatic { value: false }),
};
update.apply(context()?, ())?;
Ok(())
}

View File

@ -26,6 +26,9 @@ pub enum PackageError {
#[error("Failed to initialize package {:?} ({:?})", _0, _1)]
FailedToInitialize(String, OsString),
#[error("Invalid project name: {:?}", _0)]
InvalidPackageName(String),
#[error("`{}` metadata: {}", _0, _1)]
Removing(&'static str, io::Error),
}

View File

@ -18,6 +18,9 @@ use std::io;
#[derive(Debug, Error)]
pub enum ManifestError {
#[error("{}: {}", _0, _1)]
Crate(&'static str, String),
#[error("`{}` creating: {}", _0, _1)]
Creating(&'static str, io::Error),
@ -36,3 +39,9 @@ pub enum ManifestError {
#[error("`{}` writing: {}", _0, _1)]
Writing(&'static str, io::Error),
}
impl From<crate::errors::PackageError> for ManifestError {
fn from(error: crate::errors::PackageError) -> Self {
ManifestError::Crate("leo-package", error.to_string())
}
}

View File

@ -37,6 +37,11 @@ impl LeoPackage {
package::Package::initialize(package_name, is_lib, path)
}
/// Returns `true` if the given Leo package name is valid.
pub fn is_package_name_valid(package_name: &str) -> bool {
package::Package::is_package_name_valid(package_name)
}
/// Removes an imported Leo package
pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> {
package::Package::remove_imported_package(package_name, path)

View File

@ -34,17 +34,51 @@ pub struct Package {
}
impl Package {
pub fn new(package_name: &str) -> Self {
Self {
pub fn new(package_name: &str) -> Result<Self, PackageError> {
// Check that the package name is valid.
if !Self::is_package_name_valid(package_name) {
return Err(PackageError::InvalidPackageName(package_name.to_string()));
}
Ok(Self {
name: package_name.to_owned(),
version: "0.1.0".to_owned(),
description: None,
license: None,
}
})
}
/// Returns `true` if the package name is valid.
///
/// Package names must be lowercase and composed solely
/// of ASCII alphanumeric characters, and may be word-separated
/// by a single dash '-'.
pub fn is_package_name_valid(package_name: &str) -> bool {
// Check that the package name:
// 1. is lowercase,
// 2. is ASCII alphanumeric or a dash.
package_name.chars().all(|x| {
if !x.is_lowercase() {
tracing::error!("Project names must be all lowercase");
return false;
}
if x.is_ascii_alphanumeric() || x == '-' {
tracing::error!("Project names must be ASCII alphanumeric, and may be word-separated with a dash");
return false;
}
true
})
}
/// Returns `true` if a package is can be initialized at a given path.
pub fn can_initialize(package_name: &str, is_lib: bool, path: &Path) -> bool {
// Check that the package name is valid.
if !Self::is_package_name_valid(package_name) {
return false;
}
let mut result = true;
let mut existing_files = vec![];
@ -91,6 +125,11 @@ impl Package {
/// Returns `true` if a package is initialized at the given path
pub fn is_initialized(package_name: &str, is_lib: bool, path: &Path) -> bool {
// Check that the package name is valid.
if !Self::is_package_name_valid(package_name) {
return false;
}
// Check if the manifest file exists.
if !Manifest::exists_at(&path) {
return false;
@ -137,7 +176,7 @@ impl Package {
// Next, initialize this directory as a Leo package.
{
// Create the manifest file.
Manifest::new(&package_name).write_to(&path)?;
Manifest::new(&package_name)?.write_to(&path)?;
// Verify that the .gitignore file does not exist.
if !Gitignore::exists_at(&path) {

View File

@ -39,11 +39,11 @@ pub struct Manifest {
}
impl Manifest {
pub fn new(package_name: &str) -> Self {
Self {
project: Package::new(package_name),
pub fn new(package_name: &str) -> Result<Self, ManifestError> {
Ok(Self {
project: Package::new(package_name)?,
remote: None,
}
})
}
pub fn filename() -> String {

View File

@ -52,7 +52,10 @@ fn initialize_fails_with_existing_manifest() {
assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory));
// Manually add a manifest file to the `test_directory`
Manifest::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap();
Manifest::new(TEST_PACKAGE_NAME)
.unwrap()
.write_to(&test_directory)
.unwrap();
// Attempt to initialize a package at the `test_directory`
assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err());