mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-23 23:23:50 +03:00
Adds safety check for valid package names
This commit is contained in:
parent
c61cd3459a
commit
73b550011e
@ -40,13 +40,20 @@ impl Command for Init {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
|
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
|
let package_name = path
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.ok_or_else(|| anyhow!("Project name invalid"))?
|
.ok_or_else(|| anyhow!("Project name invalid"))?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_string();
|
.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() {
|
if !path.exists() {
|
||||||
return Err(anyhow!("Directory does not exist"));
|
return Err(anyhow!("Directory does not exist"));
|
||||||
}
|
}
|
||||||
|
@ -43,13 +43,17 @@ impl Command for New {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply(self, _: Context, _: Self::Input) -> Result<Self::Output> {
|
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;
|
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);
|
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() {
|
if path.exists() {
|
||||||
return Err(anyhow!("Directory already exists {:?}", path));
|
return Err(anyhow!("Directory already exists {:?}", path));
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ use tracing::span::Span;
|
|||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
pub struct Prove {
|
pub struct Prove {
|
||||||
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
|
#[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 {
|
impl Command for Prove {
|
||||||
|
@ -30,7 +30,7 @@ use tracing::span::Span;
|
|||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
pub struct Run {
|
pub struct Run {
|
||||||
#[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")]
|
#[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 {
|
impl Command for Run {
|
||||||
|
@ -35,7 +35,7 @@ use tracing::span::Span;
|
|||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
pub struct Setup {
|
pub struct Setup {
|
||||||
#[structopt(long = "skip-key-check", help = "Skip key verification")]
|
#[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 {
|
impl Command for Setup {
|
||||||
|
@ -36,7 +36,7 @@ use tracing::span::Span;
|
|||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
pub struct Test {
|
pub struct Test {
|
||||||
#[structopt(short = "f", long = "file", name = "file")]
|
#[structopt(short = "f", long = "file", name = "file")]
|
||||||
files: Vec<PathBuf>,
|
pub(crate) files: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for Test {
|
impl Command for Test {
|
||||||
|
@ -35,15 +35,15 @@ pub enum Automatic {
|
|||||||
pub struct Update {
|
pub struct Update {
|
||||||
/// List all available versions of Leo
|
/// List all available versions of Leo
|
||||||
#[structopt(short, long)]
|
#[structopt(short, long)]
|
||||||
list: bool,
|
pub(crate) list: bool,
|
||||||
|
|
||||||
/// For Aleo Studio only
|
/// For Aleo Studio only
|
||||||
#[structopt(short, long)]
|
#[structopt(short, long)]
|
||||||
studio: bool,
|
pub(crate) studio: bool,
|
||||||
|
|
||||||
/// Setting for automatic updates of Leo
|
/// Setting for automatic updates of Leo
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
automatic: Option<Automatic>,
|
pub(crate) automatic: Option<Automatic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for Update {
|
impl Command for Update {
|
||||||
|
@ -39,34 +39,34 @@ const PEDERSEN_HASH_PATH: &str = "./examples/pedersen-hash/";
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn build_pedersen_hash() -> Result<()> {
|
pub fn build_pedersen_hash() -> Result<()> {
|
||||||
Build::new().apply(context()?, ())?;
|
(Build {}).apply(context()?, ())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn setup_pedersen_hash() -> Result<()> {
|
pub fn setup_pedersen_hash() -> Result<()> {
|
||||||
let build = Build::new().apply(context()?, ())?;
|
let build = (Build {}).apply(context()?, ())?;
|
||||||
Setup::new(false).apply(context()?, build.clone())?;
|
(Setup { skip_key_check: false }).apply(context()?, build.clone())?;
|
||||||
Setup::new(true).apply(context()?, build)?;
|
(Setup { skip_key_check: true }).apply(context()?, build)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn prove_pedersen_hash() -> Result<()> {
|
pub fn prove_pedersen_hash() -> Result<()> {
|
||||||
let build = Build::new().apply(context()?, ())?;
|
let build = (Build {}).apply(context()?, ())?;
|
||||||
let setup = Setup::new(false).apply(context()?, build)?;
|
let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
|
||||||
Prove::new(false).apply(context()?, setup.clone())?;
|
(Prove { skip_key_check: false }).apply(context()?, setup.clone())?;
|
||||||
Prove::new(true).apply(context()?, setup)?;
|
(Prove { skip_key_check: true }).apply(context()?, setup)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn run_pedersen_hash() -> Result<()> {
|
pub fn run_pedersen_hash() -> Result<()> {
|
||||||
let build = Build::new().apply(context()?, ())?;
|
let build = (Build {}).apply(context()?, ())?;
|
||||||
let setup = Setup::new(false).apply(context()?, build)?;
|
let setup = (Setup { skip_key_check: false }).apply(context()?, build)?;
|
||||||
let prove = Prove::new(false).apply(context()?, setup)?;
|
let prove = (Prove { skip_key_check: false }).apply(context()?, setup)?;
|
||||||
Run::new(false).apply(context()?, prove.clone())?;
|
(Run { skip_key_check: false }).apply(context()?, prove.clone())?;
|
||||||
Run::new(true).apply(context()?, prove)?;
|
(Run { skip_key_check: true }).apply(context()?, prove)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,14 +75,14 @@ pub fn test_pedersen_hash() -> Result<()> {
|
|||||||
let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH);
|
let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH);
|
||||||
main_file.push("src/main.leo");
|
main_file.push("src/main.leo");
|
||||||
|
|
||||||
Test::new(Vec::new()).apply(context()?, ())?;
|
(Test { files: vec![] }).apply(context()?, ())?;
|
||||||
Test::new(vec![main_file]).apply(context()?, ())?;
|
(Test { files: vec![main_file] }).apply(context()?, ())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_logout() -> Result<()> {
|
pub fn test_logout() -> Result<()> {
|
||||||
Logout::new().apply(context()?, ())?;
|
(Logout {}).apply(context()?, ())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,12 +111,40 @@ pub fn login_incorrect_credentials_or_token() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn leo_update_and_update_automatic() -> Result<()> {
|
pub fn leo_update_and_update_automatic() -> Result<()> {
|
||||||
Update::new(true, true, None).apply(context()?, ())?;
|
let update = Update {
|
||||||
Update::new(false, true, None).apply(context()?, ())?;
|
list: true,
|
||||||
Update::new(false, false, None).apply(context()?, ())?;
|
studio: true,
|
||||||
|
automatic: None,
|
||||||
|
};
|
||||||
|
update.apply(context()?, ())?;
|
||||||
|
|
||||||
Update::new(false, false, Some(UpdateAutomatic::Automatic { value: true })).apply(context()?, ())?;
|
let update = Update {
|
||||||
Update::new(false, false, Some(UpdateAutomatic::Automatic { value: false })).apply(context()?, ())?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ pub enum PackageError {
|
|||||||
#[error("Failed to initialize package {:?} ({:?})", _0, _1)]
|
#[error("Failed to initialize package {:?} ({:?})", _0, _1)]
|
||||||
FailedToInitialize(String, OsString),
|
FailedToInitialize(String, OsString),
|
||||||
|
|
||||||
|
#[error("Invalid project name: {:?}", _0)]
|
||||||
|
InvalidPackageName(String),
|
||||||
|
|
||||||
#[error("`{}` metadata: {}", _0, _1)]
|
#[error("`{}` metadata: {}", _0, _1)]
|
||||||
Removing(&'static str, io::Error),
|
Removing(&'static str, io::Error),
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ use std::io;
|
|||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ManifestError {
|
pub enum ManifestError {
|
||||||
|
#[error("{}: {}", _0, _1)]
|
||||||
|
Crate(&'static str, String),
|
||||||
|
|
||||||
#[error("`{}` creating: {}", _0, _1)]
|
#[error("`{}` creating: {}", _0, _1)]
|
||||||
Creating(&'static str, io::Error),
|
Creating(&'static str, io::Error),
|
||||||
|
|
||||||
@ -36,3 +39,9 @@ pub enum ManifestError {
|
|||||||
#[error("`{}` writing: {}", _0, _1)]
|
#[error("`{}` writing: {}", _0, _1)]
|
||||||
Writing(&'static str, io::Error),
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,6 +37,11 @@ impl LeoPackage {
|
|||||||
package::Package::initialize(package_name, is_lib, path)
|
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
|
/// Removes an imported Leo package
|
||||||
pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> {
|
pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> {
|
||||||
package::Package::remove_imported_package(package_name, path)
|
package::Package::remove_imported_package(package_name, path)
|
||||||
|
@ -34,17 +34,51 @@ pub struct Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Package {
|
impl Package {
|
||||||
pub fn new(package_name: &str) -> Self {
|
pub fn new(package_name: &str) -> Result<Self, PackageError> {
|
||||||
Self {
|
// 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(),
|
name: package_name.to_owned(),
|
||||||
version: "0.1.0".to_owned(),
|
version: "0.1.0".to_owned(),
|
||||||
description: None,
|
description: None,
|
||||||
license: 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.
|
/// 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 {
|
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 result = true;
|
||||||
let mut existing_files = vec![];
|
let mut existing_files = vec![];
|
||||||
|
|
||||||
@ -91,6 +125,11 @@ impl Package {
|
|||||||
|
|
||||||
/// Returns `true` if a package is initialized at the given path
|
/// Returns `true` if a package is initialized at the given path
|
||||||
pub fn is_initialized(package_name: &str, is_lib: bool, path: &Path) -> bool {
|
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.
|
// Check if the manifest file exists.
|
||||||
if !Manifest::exists_at(&path) {
|
if !Manifest::exists_at(&path) {
|
||||||
return false;
|
return false;
|
||||||
@ -137,7 +176,7 @@ impl Package {
|
|||||||
// Next, initialize this directory as a Leo package.
|
// Next, initialize this directory as a Leo package.
|
||||||
{
|
{
|
||||||
// Create the manifest file.
|
// 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.
|
// Verify that the .gitignore file does not exist.
|
||||||
if !Gitignore::exists_at(&path) {
|
if !Gitignore::exists_at(&path) {
|
||||||
|
@ -39,11 +39,11 @@ pub struct Manifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Manifest {
|
impl Manifest {
|
||||||
pub fn new(package_name: &str) -> Self {
|
pub fn new(package_name: &str) -> Result<Self, ManifestError> {
|
||||||
Self {
|
Ok(Self {
|
||||||
project: Package::new(package_name),
|
project: Package::new(package_name)?,
|
||||||
remote: None,
|
remote: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filename() -> String {
|
pub fn filename() -> String {
|
||||||
|
@ -52,7 +52,10 @@ fn initialize_fails_with_existing_manifest() {
|
|||||||
assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory));
|
assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory));
|
||||||
|
|
||||||
// Manually add a manifest file to the `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`
|
// Attempt to initialize a package at the `test_directory`
|
||||||
assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err());
|
assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err());
|
||||||
|
Loading…
Reference in New Issue
Block a user