From 0f86b58e9278c8ba5f99c323fd24bfe707c3029c Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Sun, 13 Feb 2022 15:12:00 +1300 Subject: [PATCH 01/30] cli: Error in build step on missing docstring for AccountInfo and UncheckedAccount --- lang/syn/src/parser/context.rs | 64 ++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 42ad1b5710..cd87bcaa0c 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -1,6 +1,6 @@ +use anyhow::anyhow; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; - use syn::parse::{Error as ParseError, Result as ParseResult}; /// Crate parse context @@ -36,9 +36,47 @@ impl CrateContext { } pub fn parse(root: impl AsRef) -> Result { - Ok(CrateContext { + let crate_context = CrateContext { modules: ParsedModule::parse_recursive(root.as_ref())?, - }) + }; + crate_context.safety_checks()?; + Ok(crate_context) + } + + // Perform Anchor safety checks on the parsed create + pub fn safety_checks(&self) -> Result<(), anyhow::Error> { + // Check all structs for unsafe field types, i.e. AccountInfo and UncheckedAccount. + let unsafe_struct_fields = self + .modules + .iter() + .flat_map(|(_, ctx)| ctx.unsafe_struct_fields()); + + for unsafe_field in unsafe_struct_fields { + // Check if unsafe field type has been documented with a /// SAFETY: doc string. + let is_documented = + unsafe_field + .attrs + .iter() + .any(|attr| match attr.parse_meta().unwrap() { + syn::Meta::NameValue(syn::MetaNameValue { + lit: syn::Lit::Str(lit), + .. + }) => lit.value().contains("SAFETY:"), + _ => false, + }); + if !is_documented { + // Error if undocumented. + return Err(anyhow!( + r#" + Struct field "{}" is unsafe, but is not documented. + Please add a `#[doc(SAFETY: "...")]` attribute to the field enumerating potential security risks. + See https://book.anchor-lang.com/chapter_4/cli.html#safety_checks for more information. + "#, + unsafe_field.ident.as_ref().unwrap().to_string() + )); + }; + } + Ok(()) } } @@ -181,6 +219,26 @@ impl ParsedModule { }) } + fn unsafe_struct_fields(&self) -> impl Iterator { + self.structs() + .flat_map(|s| &s.fields) + .filter_map(|f| match &f.ty { + syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) => { + if segments.len() == 1 && segments[0].ident == "UncheckedAccount" + || segments[0].ident == "AccountInfo" + { + Some(f) + } else { + None + } + } + _ => None, + }) + } + fn enums(&self) -> impl Iterator { self.items.iter().filter_map(|i| match i { syn::Item::Enum(item) => Some(item), From 11ca09609cdd8d149768c692b3c872819c70f3bf Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Mon, 14 Feb 2022 17:05:04 +1300 Subject: [PATCH 02/30] Refactor to avoid parse_meta, allow account attribute with seeds --- lang/syn/src/parser/context.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index cd87bcaa0c..0941b09cd1 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -53,23 +53,28 @@ impl CrateContext { for unsafe_field in unsafe_struct_fields { // Check if unsafe field type has been documented with a /// SAFETY: doc string. - let is_documented = - unsafe_field - .attrs - .iter() - .any(|attr| match attr.parse_meta().unwrap() { - syn::Meta::NameValue(syn::MetaNameValue { - lit: syn::Lit::Str(lit), - .. - }) => lit.value().contains("SAFETY:"), - _ => false, - }); + let is_documented = unsafe_field.attrs.iter().any(|attr| { + attr.tokens.clone().into_iter().any(|token| match token { + // Check for comments containing SAFETY: or CHECK: + proc_macro2::TokenTree::Literal(s) => { + s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") + } + // Allow valid PDAs without documentation + proc_macro2::TokenTree::Group(g) => { + g.stream().into_iter().any(|token| match token { + proc_macro2::TokenTree::Ident(s) => s.to_string() == "seeds", + _ => false, + }) + } + _ => false, + }) + }); if !is_documented { // Error if undocumented. return Err(anyhow!( r#" Struct field "{}" is unsafe, but is not documented. - Please add a `#[doc(SAFETY: "...")]` attribute to the field enumerating potential security risks. + Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. See https://book.anchor-lang.com/chapter_4/cli.html#safety_checks for more information. "#, unsafe_field.ident.as_ref().unwrap().to_string() From 72e9088a401e3619d757219438830078fdc70302 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Tue, 15 Feb 2022 15:28:00 +1300 Subject: [PATCH 03/30] Attempt at printing span locations --- lang/syn/Cargo.toml | 2 +- lang/syn/src/parser/context.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lang/syn/Cargo.toml b/lang/syn/Cargo.toml index ee98c3472d..73a228d2ce 100644 --- a/lang/syn/Cargo.toml +++ b/lang/syn/Cargo.toml @@ -16,7 +16,7 @@ anchor-debug = [] seeds = [] [dependencies] -proc-macro2 = "1.0" +proc-macro2 = { version = "1.0", features=["span-locations"]} proc-macro2-diagnostics = "0.9" quote = "1.0" syn = { version = "1.0.60", features = ["full", "extra-traits", "parsing"] } diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 0941b09cd1..396f5bb2af 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -70,14 +70,18 @@ impl CrateContext { }) }); if !is_documented { + let ident = unsafe_field.ident.as_ref().unwrap(); + let span = ident.span(); // Error if undocumented. return Err(anyhow!( r#" - Struct field "{}" is unsafe, but is not documented. + {}:{} struct field "{}" is unsafe, but is not documented. Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. See https://book.anchor-lang.com/chapter_4/cli.html#safety_checks for more information. "#, - unsafe_field.ident.as_ref().unwrap().to_string() + span.start().line, + span.start().column, + ident.to_string() )); }; } From ff5d58413dc082232b413e9594a23cd082f22e7c Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Tue, 15 Feb 2022 16:31:59 +1300 Subject: [PATCH 04/30] Refactor loop to support filename --- lang/syn/src/parser/context.rs | 75 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 396f5bb2af..d73a5aa82e 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -46,44 +46,43 @@ impl CrateContext { // Perform Anchor safety checks on the parsed create pub fn safety_checks(&self) -> Result<(), anyhow::Error> { // Check all structs for unsafe field types, i.e. AccountInfo and UncheckedAccount. - let unsafe_struct_fields = self - .modules - .iter() - .flat_map(|(_, ctx)| ctx.unsafe_struct_fields()); - - for unsafe_field in unsafe_struct_fields { - // Check if unsafe field type has been documented with a /// SAFETY: doc string. - let is_documented = unsafe_field.attrs.iter().any(|attr| { - attr.tokens.clone().into_iter().any(|token| match token { - // Check for comments containing SAFETY: or CHECK: - proc_macro2::TokenTree::Literal(s) => { - s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") - } - // Allow valid PDAs without documentation - proc_macro2::TokenTree::Group(g) => { - g.stream().into_iter().any(|token| match token { - proc_macro2::TokenTree::Ident(s) => s.to_string() == "seeds", - _ => false, - }) - } - _ => false, - }) - }); - if !is_documented { - let ident = unsafe_field.ident.as_ref().unwrap(); - let span = ident.span(); - // Error if undocumented. - return Err(anyhow!( - r#" - {}:{} struct field "{}" is unsafe, but is not documented. - Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. - See https://book.anchor-lang.com/chapter_4/cli.html#safety_checks for more information. - "#, - span.start().line, - span.start().column, - ident.to_string() - )); - }; + for (_, ctx) in self.modules.iter() { + for unsafe_field in ctx.unsafe_struct_fields() { + // Check if unsafe field type has been documented with a /// SAFETY: doc string. + let is_documented = unsafe_field.attrs.iter().any(|attr| { + attr.tokens.clone().into_iter().any(|token| match token { + // Check for comments containing SAFETY: or CHECK: + proc_macro2::TokenTree::Literal(s) => { + s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") + } + // Allow valid PDAs without documentation + proc_macro2::TokenTree::Group(g) => { + g.stream().into_iter().any(|token| match token { + proc_macro2::TokenTree::Ident(s) => s.to_string() == "seeds", + _ => false, + }) + } + _ => false, + }) + }); + if !is_documented { + let ident = unsafe_field.ident.as_ref().unwrap(); + let span = ident.span(); + // Error if undocumented. + return Err(anyhow!( + r#" + {}:{}:{} + Struct field "{}" is unsafe, but is not documented. + Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. + See https://book.anchor-lang.com/chapter_4/cli.html#safety_checks for more information. + "#, + ctx.file.canonicalize().unwrap().display(), + span.start().line, + span.start().column, + ident.to_string() + )); + }; + } } Ok(()) } From 440c9e3c062d95bfd89bdb012625b4071654a01f Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Tue, 15 Feb 2022 17:02:08 +1300 Subject: [PATCH 05/30] Clarify arg --- lang/syn/src/parser/context.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index d73a5aa82e..3bd47a5da8 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -49,23 +49,24 @@ impl CrateContext { for (_, ctx) in self.modules.iter() { for unsafe_field in ctx.unsafe_struct_fields() { // Check if unsafe field type has been documented with a /// SAFETY: doc string. - let is_documented = unsafe_field.attrs.iter().any(|attr| { + let is_documented_or_safe = unsafe_field.attrs.iter().any(|attr| { attr.tokens.clone().into_iter().any(|token| match token { // Check for comments containing SAFETY: or CHECK: proc_macro2::TokenTree::Literal(s) => { s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") } - // Allow valid PDAs without documentation + // Allow without doc comment if certain account constraints are present proc_macro2::TokenTree::Group(g) => { g.stream().into_iter().any(|token| match token { - proc_macro2::TokenTree::Ident(s) => s.to_string() == "seeds", + // TODO is it safe to skip in the case of init? + proc_macro2::TokenTree::Ident(s) => s.to_string() == "init", _ => false, }) } _ => false, }) }); - if !is_documented { + if !is_documented_or_safe { let ident = unsafe_field.ident.as_ref().unwrap(); let span = ident.span(); // Error if undocumented. From 471ba5a9895e6113d794d6e515cd22510a3378c7 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Tue, 15 Feb 2022 17:03:31 +1300 Subject: [PATCH 06/30] Add test to misc --- tests/misc/programs/misc/src/context.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index f4a9504ffd..7b236ff75c 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -409,3 +409,23 @@ pub struct TestProgramIdConstraintUsingFindPda<'info> { #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] second: AccountInfo<'info>, } + +#[derive(Accounts)] +pub struct TestUnsafeFieldSafetyErrors<'info> { + #[doc = "test"] + /// SAFETY: + pub data: UncheckedAccount<'info>, + #[account(mut)] + /// SAFETY: + pub data_two: UncheckedAccount<'info>, + #[account( + seeds = [b"my-seed", signer.key.as_ref()], + bump + )] + // Doesn't need a SAFETY doc comment because it is a PDA + pub data_three: UncheckedAccount<'info>, + /// SAFETY: + pub data_four: UncheckedAccount<'info>, + pub signer: Signer<'info>, + pub system_program: Program<'info, System>, +} From 7ef9d5133fdaf41103a16e673fbddcde24f27e71 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Tue, 15 Feb 2022 17:03:48 +1300 Subject: [PATCH 07/30] Add SAFETY doc comments to some tests --- tests/cfo/programs/cfo/src/lib.rs | 25 +++++++++++++++++++++++-- tests/misc/programs/misc/src/context.rs | 21 +++++++++++++++++++++ tests/misc/programs/misc2/src/lib.rs | 1 + 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/cfo/programs/cfo/src/lib.rs b/tests/cfo/programs/cfo/src/lib.rs index 88cba51c95..cf2063eb50 100644 --- a/tests/cfo/programs/cfo/src/lib.rs +++ b/tests/cfo/programs/cfo/src/lib.rs @@ -396,7 +396,7 @@ pub struct AuthorizeMarket<'info> { market_auth: Account<'info, MarketAuth>, #[account(mut)] payer: Signer<'info>, - // Not read or written to so not validated. + /// SAFETY: Not read or written to so not validated. market: UncheckedAccount<'info>, system_program: Program<'info, System>, } @@ -450,7 +450,7 @@ pub struct CreateOfficerOpenOrders<'info> { dex_program: Program<'info, Dex>, system_program: Program<'info, System>, rent: Sysvar<'info, Rent>, - // Used for CPI. Not read or written so not validated. + /// SAFETY: Used for CPI. Not read or written so not validated. market: UncheckedAccount<'info>, } @@ -483,10 +483,14 @@ pub struct SweepFees<'info> { #[derive(Accounts)] pub struct DexAccounts<'info> { #[account(mut)] + /// SAFETY: market: UncheckedAccount<'info>, #[account(mut)] + /// SAFETY: pc_vault: UncheckedAccount<'info>, + /// SAFETY: sweep_authority: UncheckedAccount<'info>, + /// SAFETY: vault_signer: UncheckedAccount<'info>, dex_program: Program<'info, Dex>, token_program: Program<'info, Token>, @@ -523,6 +527,7 @@ pub struct SwapToUsdc<'info> { dex_program: Program<'info, Dex>, token_program: Program<'info, Token>, #[account(address = tx_instructions::ID)] + /// SAFETY: instructions: UncheckedAccount<'info>, rent: Sysvar<'info, Rent>, } @@ -561,6 +566,7 @@ pub struct SwapToSrm<'info> { dex_program: Program<'info, Dex>, token_program: Program<'info, Token>, #[account(address = tx_instructions::ID)] + /// SAFETY: instructions: UncheckedAccount<'info>, rent: Sysvar<'info, Rent>, } @@ -569,32 +575,42 @@ pub struct SwapToSrm<'info> { // They are not read or written and so are not validated. #[derive(Accounts)] pub struct DexMarketAccounts<'info> { + /// SAFETY: #[account(mut)] market: UncheckedAccount<'info>, + /// SAFETY: #[account(mut)] open_orders: UncheckedAccount<'info>, + /// SAFETY: #[account(mut)] request_queue: UncheckedAccount<'info>, + /// SAFETY: #[account(mut)] event_queue: UncheckedAccount<'info>, + /// SAFETY: #[account(mut)] bids: UncheckedAccount<'info>, + /// SAFETY: #[account(mut)] asks: UncheckedAccount<'info>, + /// SAFETY: // The `spl_token::Account` that funds will be taken from, i.e., transferred // from the user into the market's vault. // // For bids, this is the base currency. For asks, the quote. #[account(mut)] order_payer_token_account: UncheckedAccount<'info>, + /// SAFETY: // Also known as the "base" currency. For a given A/B market, // this is the vault for the A mint. #[account(mut)] coin_vault: UncheckedAccount<'info>, + /// SAFETY: // Also known as the "quote" currency. For a given A/B market, // this is the vault for the B mint. #[account(mut)] pc_vault: UncheckedAccount<'info>, + /// SAFETY: // PDA owner of the DEX's token accounts for base + quote currencies. vault_signer: UncheckedAccount<'info>, } @@ -636,6 +652,7 @@ pub struct DropStakeReward<'info> { not(feature = "test"), account(address = mint::SRM), )] + /// SAFETY: mint: UncheckedAccount<'info>, srm: DropStakeRewardPool<'info>, msrm: DropStakeRewardPool<'info>, @@ -652,10 +669,14 @@ pub struct DropStakeReward<'info> { // program to handle it. #[derive(Accounts)] pub struct DropStakeRewardPool<'info> { + /// SAFETY: registrar: UncheckedAccount<'info>, + /// SAFETY: reward_event_q: UncheckedAccount<'info>, pool_mint: Account<'info, Mint>, + /// SAFETY: vendor: UncheckedAccount<'info>, + /// SAFETY: vendor_vault: UncheckedAccount<'info>, } diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 7b236ff75c..68bd1b0f34 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -28,6 +28,7 @@ pub struct TestTokenSeedsInit<'info> { )] pub my_pda: Account<'info, TokenAccount>, #[account(mut)] + /// SAFETY: pub authority: AccountInfo<'info>, pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, @@ -60,6 +61,7 @@ pub struct TestValidateAssociatedToken<'info> { )] pub token: Account<'info, TokenAccount>, pub mint: Account<'info, Mint>, + /// SAFETY: pub wallet: AccountInfo<'info>, } @@ -71,6 +73,7 @@ pub struct TestInstructionConstraint<'info> { bump = nonce, )] pub my_pda: AccountInfo<'info>, + /// SAFETY: pub my_account: AccountInfo<'info>, } @@ -86,6 +89,7 @@ pub struct TestPdaInit<'info> { pub my_pda: Account<'info, DataU16>, #[account(mut)] pub my_payer: Signer<'info>, + /// SAFETY: pub foo: AccountInfo<'info>, pub system_program: Program<'info, System>, } @@ -112,6 +116,7 @@ pub struct TestPdaMutZeroCopy<'info> { bump = my_pda.load()?.bump, )] pub my_pda: Loader<'info, DataZeroCopy>, + /// SAFETY: pub my_payer: AccountInfo<'info>, } @@ -135,29 +140,35 @@ pub struct InitializeSkipRentExempt<'info> { #[derive(Accounts)] pub struct InitializeNoRentExempt<'info> { + /// SAFETY: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestOwner<'info> { #[account(owner = *misc.key)] + /// SAFETY: pub data: AccountInfo<'info>, + /// SAFETY: pub misc: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestExecutable<'info> { #[account(executable)] + /// SAFETY: pub program: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestStateCpi<'info> { #[account(signer)] + /// SAFETY: pub authority: AccountInfo<'info>, #[account(mut, state = misc2_program)] pub cpi_state: CpiState<'info, Misc2State>, #[account(executable)] + /// SAFETY: pub misc2_program: AccountInfo<'info>, } @@ -165,6 +176,7 @@ pub struct TestStateCpi<'info> { pub struct TestClose<'info> { #[account(mut, close = sol_dest)] pub data: Account<'info, Data>, + /// SAFETY: sol_dest: AccountInfo<'info>, } @@ -287,6 +299,7 @@ pub struct TestInitIfNeededChecksOwner<'info> { #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>, + /// SAFETY: pub owner: AccountInfo<'info>, } @@ -310,7 +323,9 @@ pub struct TestInitMintIfNeeded<'info> { pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, + /// SAFETY: pub mint_authority: AccountInfo<'info>, + /// SAFETY: pub freeze_authority: AccountInfo<'info>, } @@ -324,6 +339,7 @@ pub struct TestInitTokenIfNeeded<'info> { pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, + /// SAFETY: pub authority: AccountInfo<'info>, } @@ -343,6 +359,7 @@ pub struct TestInitAssociatedTokenIfNeeded<'info> { pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, pub associated_token_program: Program<'info, AssociatedToken>, + /// SAFETY: pub authority: AccountInfo<'info>, } @@ -360,18 +377,21 @@ pub struct TestConstArraySize<'info> { #[derive(Accounts)] pub struct NoRentExempt<'info> { + /// SAFETY: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct EnforceRentExempt<'info> { #[account(rent_exempt = enforce)] + /// SAFETY: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct InitDecreaseLamports<'info> { #[account(init, payer = user, space = 1000)] + /// SAFETY: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, @@ -381,6 +401,7 @@ pub struct InitDecreaseLamports<'info> { #[derive(Accounts)] pub struct InitIfNeededChecksRentExemption<'info> { #[account(init_if_needed, payer = user, space = 1000)] + /// SAFETY: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, diff --git a/tests/misc/programs/misc2/src/lib.rs b/tests/misc/programs/misc2/src/lib.rs index 0b97eb01ab..77e5029295 100644 --- a/tests/misc/programs/misc2/src/lib.rs +++ b/tests/misc/programs/misc2/src/lib.rs @@ -33,5 +33,6 @@ pub mod misc2 { #[derive(Accounts)] pub struct Auth<'info> { #[account(signer)] + /// SAFETY: pub authority: AccountInfo<'info>, } From 369c33360ccccb8cc99cbdea591612c21698bcae Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 13:32:24 +1300 Subject: [PATCH 08/30] Add safety_checks to features --- cli/src/config.rs | 8 ++++++++ cli/src/lib.rs | 7 ++++++- lang/syn/src/idl/file.rs | 4 ++++ lang/syn/src/parser/context.rs | 6 ++---- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cli/src/config.rs b/cli/src/config.rs index 828e21d3f8..48f6b9c1d9 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -166,6 +166,7 @@ impl WithPath { path.join("src/lib.rs"), version, self.features.seeds, + self.features.safety_checks, )?; r.push(Program { lib_name, @@ -259,6 +260,13 @@ pub struct Config { #[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct FeaturesConfig { pub seeds: bool, + #[serde(default = "default_safety_checks")] + pub safety_checks: bool, +} + +// Anchor safety checks on by default +fn default_safety_checks() -> bool { + true } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 6460219379..a126020080 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1388,7 +1388,12 @@ fn extract_idl(cfg: &WithPath, file: &str) -> Result> { let manifest_from_path = std::env::current_dir()?.join(PathBuf::from(&*file).parent().unwrap()); let cargo = Manifest::discover_from_path(manifest_from_path)? .ok_or_else(|| anyhow!("Cargo.toml not found"))?; - anchor_syn::idl::file::parse(&*file, cargo.version(), cfg.features.seeds) + anchor_syn::idl::file::parse( + &*file, + cargo.version(), + cfg.features.seeds, + cfg.features.safety_checks, + ) } fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> { diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs index 119f173a5c..d3759c7603 100644 --- a/lang/syn/src/idl/file.rs +++ b/lang/syn/src/idl/file.rs @@ -18,8 +18,12 @@ pub fn parse( filename: impl AsRef, version: String, seeds_feature: bool, + safety_checks: bool, ) -> Result> { let ctx = CrateContext::parse(filename)?; + if safety_checks { + ctx.safety_checks()?; + } let program_mod = match parse_program_mod(&ctx) { None => return Ok(None), diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 3bd47a5da8..3884607046 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -36,11 +36,9 @@ impl CrateContext { } pub fn parse(root: impl AsRef) -> Result { - let crate_context = CrateContext { + Ok(CrateContext { modules: ParsedModule::parse_recursive(root.as_ref())?, - }; - crate_context.safety_checks()?; - Ok(crate_context) + }) } // Perform Anchor safety checks on the parsed create From b2563e090f0064c85b8851cdf8e1225c10d9a36a Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 13:32:40 +1300 Subject: [PATCH 09/30] Safety fixes for misc --- tests/misc/programs/misc/src/context.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 68bd1b0f34..7443360e8f 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -72,6 +72,7 @@ pub struct TestInstructionConstraint<'info> { seeds = [b"my-seed", my_account.key.as_ref()], bump = nonce, )] + /// SAFETY: pub my_pda: AccountInfo<'info>, /// SAFETY: pub my_account: AccountInfo<'info>, @@ -271,6 +272,7 @@ pub struct TestInitWithEmptySeeds<'info> { #[derive(Accounts)] pub struct TestEmptySeedsConstraint<'info> { #[account(seeds = [], bump)] + /// SAFETY: pub pda: AccountInfo<'info>, } @@ -295,6 +297,7 @@ pub struct TestInitIfNeeded<'info> { #[derive(Accounts)] pub struct TestInitIfNeededChecksOwner<'info> { #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] + /// SAFETY: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -307,6 +310,7 @@ pub struct TestInitIfNeededChecksOwner<'info> { #[instruction(seed_data: String)] pub struct TestInitIfNeededChecksSeeds<'info> { #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] + /// SAFETY: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -414,9 +418,11 @@ pub struct TestProgramIdConstraint<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] + /// SAFETY: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] + /// SAFETY: second: AccountInfo<'info>, } @@ -425,9 +431,11 @@ pub struct TestProgramIdConstraintUsingFindPda<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] + /// SAFETY: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] + /// SAFETY: second: AccountInfo<'info>, } @@ -443,7 +451,7 @@ pub struct TestUnsafeFieldSafetyErrors<'info> { seeds = [b"my-seed", signer.key.as_ref()], bump )] - // Doesn't need a SAFETY doc comment because it is a PDA + /// SAFETY: pub data_three: UncheckedAccount<'info>, /// SAFETY: pub data_four: UncheckedAccount<'info>, From 67df10ecbbe078efe735de86cd0435ac290b5335 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 13:42:03 +1300 Subject: [PATCH 10/30] Update book link --- lang/syn/src/parser/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 3884607046..5db7128272 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -73,7 +73,7 @@ impl CrateContext { {}:{}:{} Struct field "{}" is unsafe, but is not documented. Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. - See https://book.anchor-lang.com/chapter_4/cli.html#safety_checks for more information. + See https://book.anchor-lang.com/chapter_3/errors.html#safety-checks for more information. "#, ctx.file.canonicalize().unwrap().display(), span.start().line, From 78f0cdf9fc4d972e14cc8f10baacc2e8aae42038 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 14:09:24 +1300 Subject: [PATCH 11/30] Remove init allowance --- lang/syn/src/parser/context.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 5db7128272..8581c18ea2 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -53,14 +53,6 @@ impl CrateContext { proc_macro2::TokenTree::Literal(s) => { s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") } - // Allow without doc comment if certain account constraints are present - proc_macro2::TokenTree::Group(g) => { - g.stream().into_iter().any(|token| match token { - // TODO is it safe to skip in the case of init? - proc_macro2::TokenTree::Ident(s) => s.to_string() == "init", - _ => false, - }) - } _ => false, }) }); From 95c662bb9bd9f18900f0f208306e0703b0dce06d Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 14:16:19 +1300 Subject: [PATCH 12/30] No check on read_all_programs --- cli/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/config.rs b/cli/src/config.rs index 48f6b9c1d9..d38eaf7d7a 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -166,7 +166,7 @@ impl WithPath { path.join("src/lib.rs"), version, self.features.seeds, - self.features.safety_checks, + false, )?; r.push(Program { lib_name, From e18863f92b5daa7c57179d479270975ccbe4dda6 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Wed, 16 Feb 2022 14:31:45 +1300 Subject: [PATCH 13/30] Clippy --- lang/syn/src/parser/context.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 8581c18ea2..8c2081389e 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -221,20 +221,15 @@ impl ParsedModule { fn unsafe_struct_fields(&self) -> impl Iterator { self.structs() .flat_map(|s| &s.fields) - .filter_map(|f| match &f.ty { + .filter(|f| match &f.ty { syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) => { - if segments.len() == 1 && segments[0].ident == "UncheckedAccount" + segments.len() == 1 && segments[0].ident == "UncheckedAccount" || segments[0].ident == "AccountInfo" - { - Some(f) - } else { - None - } } - _ => None, + _ => false, }) } From e162567a92f3b1f2337241f15ec9c5abcfbe4edd Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 10:06:44 +1300 Subject: [PATCH 14/30] Update error message --- lang/syn/src/parser/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 8c2081389e..829732dc6f 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -64,7 +64,7 @@ impl CrateContext { r#" {}:{}:{} Struct field "{}" is unsafe, but is not documented. - Please add a `/// SAFETY:` doc comment to the field enumerating potential security risks. + Please add a `/// SAFETY:` doc comment explaining why no checks through types are necessary. See https://book.anchor-lang.com/chapter_3/errors.html#safety-checks for more information. "#, ctx.file.canonicalize().unwrap().display(), From e1490c273537e87012856297a92e307486ea975d Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 10:26:28 +1300 Subject: [PATCH 15/30] Dont require check on seeds or init constraints --- lang/syn/src/parser/context.rs | 10 ++++++++++ tests/misc/programs/misc/src/context.rs | 9 --------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 829732dc6f..7955633b14 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -53,6 +53,16 @@ impl CrateContext { proc_macro2::TokenTree::Literal(s) => { s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") } + proc_macro2::TokenTree::Group(g) => { + g.stream().into_iter().any(|token| match token { + proc_macro2::TokenTree::Ident(s) => { + // Allow PDAs and accounts which are being initialized without + // doc comments + s.to_string() == "seeds" || s.to_string() == "init" + } + _ => false, + }) + } _ => false, }) }); diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 7443360e8f..25f7c9b375 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -72,7 +72,6 @@ pub struct TestInstructionConstraint<'info> { seeds = [b"my-seed", my_account.key.as_ref()], bump = nonce, )] - /// SAFETY: pub my_pda: AccountInfo<'info>, /// SAFETY: pub my_account: AccountInfo<'info>, @@ -297,7 +296,6 @@ pub struct TestInitIfNeeded<'info> { #[derive(Accounts)] pub struct TestInitIfNeededChecksOwner<'info> { #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] - /// SAFETY: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -310,7 +308,6 @@ pub struct TestInitIfNeededChecksOwner<'info> { #[instruction(seed_data: String)] pub struct TestInitIfNeededChecksSeeds<'info> { #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] - /// SAFETY: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -395,7 +392,6 @@ pub struct EnforceRentExempt<'info> { #[derive(Accounts)] pub struct InitDecreaseLamports<'info> { #[account(init, payer = user, space = 1000)] - /// SAFETY: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, @@ -418,11 +414,9 @@ pub struct TestProgramIdConstraint<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] - /// SAFETY: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] - /// SAFETY: second: AccountInfo<'info>, } @@ -431,11 +425,9 @@ pub struct TestProgramIdConstraintUsingFindPda<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] - /// SAFETY: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] - /// SAFETY: second: AccountInfo<'info>, } @@ -451,7 +443,6 @@ pub struct TestUnsafeFieldSafetyErrors<'info> { seeds = [b"my-seed", signer.key.as_ref()], bump )] - /// SAFETY: pub data_three: UncheckedAccount<'info>, /// SAFETY: pub data_four: UncheckedAccount<'info>, From 48288a008c4c683bdcc426d1c28a5d525a1aca01 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 10:26:41 +1300 Subject: [PATCH 16/30] Fix defaults for features in cli config --- cli/src/config.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cli/src/config.rs b/cli/src/config.rs index d38eaf7d7a..9fa0d11a63 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -257,16 +257,20 @@ pub struct Config { pub test: Option, } -#[derive(Default, Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct FeaturesConfig { pub seeds: bool, - #[serde(default = "default_safety_checks")] pub safety_checks: bool, } -// Anchor safety checks on by default -fn default_safety_checks() -> bool { - true +impl Default for FeaturesConfig { + fn default() -> Self { + Self { + seeds: false, + // Anchor safety checks on by default + safety_checks: true, + } + } } #[derive(Clone, Debug, Serialize, Deserialize)] From 72ab7f798853e3a2aa110763ab7daceea61b0ef6 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 10:34:49 +1300 Subject: [PATCH 17/30] Properly fix defaults --- cli/src/config.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/src/config.rs b/cli/src/config.rs index 9fa0d11a63..0160f51d28 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -259,7 +259,9 @@ pub struct Config { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct FeaturesConfig { + #[serde(default)] pub seeds: bool, + #[serde(default = "default_safety_checks")] pub safety_checks: bool, } @@ -273,6 +275,10 @@ impl Default for FeaturesConfig { } } +fn default_safety_checks() -> bool { + true +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RegistryConfig { pub url: String, From 2aafe6802cd391e984ddf8f135616d275c16c509 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 16:17:24 +1300 Subject: [PATCH 18/30] Update auction-house submodule --- tests/auction-house | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auction-house b/tests/auction-house index 2bfe49bdac..2b1b1e0498 160000 --- a/tests/auction-house +++ b/tests/auction-house @@ -1 +1 @@ -Subproject commit 2bfe49bdac2333d0e413a1e452c0ab7b502266fa +Subproject commit 2b1b1e04986106715ab53794bcb63d3641673f64 From a17219d2a1414e52efad15c592cf07f89616fc4f Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 16:39:27 +1300 Subject: [PATCH 19/30] Clippy --- lang/syn/src/parser/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 7955633b14..c0d736fb0b 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -58,7 +58,7 @@ impl CrateContext { proc_macro2::TokenTree::Ident(s) => { // Allow PDAs and accounts which are being initialized without // doc comments - s.to_string() == "seeds" || s.to_string() == "init" + s == "seeds" || s == "init" } _ => false, }) From c35818f37b2deb080fb7618070066ba81ad737cf Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 20:12:09 +1300 Subject: [PATCH 20/30] Disable where necessary via Anchor.toml --- tests/cfo/Anchor.toml | 3 +++ tests/cfo/programs/cfo/src/lib.rs | 25 ++----------------------- tests/chat/Anchor.toml | 3 +++ tests/custom-coder/Anchor.toml | 3 +++ tests/errors/Anchor.toml | 3 +++ tests/escrow/Anchor.toml | 3 +++ tests/ido-pool/Anchor.toml | 3 +++ tests/interface/Anchor.toml | 3 +++ tests/lockup/Anchor.toml | 3 +++ tests/pda-derivation/Anchor.toml | 1 + tests/pyth/Anchor.toml | 3 +++ tests/swap/Anchor.toml | 3 +++ tests/zero-copy/Anchor.toml | 3 +++ 13 files changed, 36 insertions(+), 23 deletions(-) diff --git a/tests/cfo/Anchor.toml b/tests/cfo/Anchor.toml index d1204d1e91..640628a366 100644 --- a/tests/cfo/Anchor.toml +++ b/tests/cfo/Anchor.toml @@ -41,3 +41,6 @@ program = "./deps/stake/target/deploy/registry.so" [[test.genesis]] address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks" program = "./deps/stake/target/deploy/lockup.so" + +[features] +safety_checks = false diff --git a/tests/cfo/programs/cfo/src/lib.rs b/tests/cfo/programs/cfo/src/lib.rs index cf2063eb50..88cba51c95 100644 --- a/tests/cfo/programs/cfo/src/lib.rs +++ b/tests/cfo/programs/cfo/src/lib.rs @@ -396,7 +396,7 @@ pub struct AuthorizeMarket<'info> { market_auth: Account<'info, MarketAuth>, #[account(mut)] payer: Signer<'info>, - /// SAFETY: Not read or written to so not validated. + // Not read or written to so not validated. market: UncheckedAccount<'info>, system_program: Program<'info, System>, } @@ -450,7 +450,7 @@ pub struct CreateOfficerOpenOrders<'info> { dex_program: Program<'info, Dex>, system_program: Program<'info, System>, rent: Sysvar<'info, Rent>, - /// SAFETY: Used for CPI. Not read or written so not validated. + // Used for CPI. Not read or written so not validated. market: UncheckedAccount<'info>, } @@ -483,14 +483,10 @@ pub struct SweepFees<'info> { #[derive(Accounts)] pub struct DexAccounts<'info> { #[account(mut)] - /// SAFETY: market: UncheckedAccount<'info>, #[account(mut)] - /// SAFETY: pc_vault: UncheckedAccount<'info>, - /// SAFETY: sweep_authority: UncheckedAccount<'info>, - /// SAFETY: vault_signer: UncheckedAccount<'info>, dex_program: Program<'info, Dex>, token_program: Program<'info, Token>, @@ -527,7 +523,6 @@ pub struct SwapToUsdc<'info> { dex_program: Program<'info, Dex>, token_program: Program<'info, Token>, #[account(address = tx_instructions::ID)] - /// SAFETY: instructions: UncheckedAccount<'info>, rent: Sysvar<'info, Rent>, } @@ -566,7 +561,6 @@ pub struct SwapToSrm<'info> { dex_program: Program<'info, Dex>, token_program: Program<'info, Token>, #[account(address = tx_instructions::ID)] - /// SAFETY: instructions: UncheckedAccount<'info>, rent: Sysvar<'info, Rent>, } @@ -575,42 +569,32 @@ pub struct SwapToSrm<'info> { // They are not read or written and so are not validated. #[derive(Accounts)] pub struct DexMarketAccounts<'info> { - /// SAFETY: #[account(mut)] market: UncheckedAccount<'info>, - /// SAFETY: #[account(mut)] open_orders: UncheckedAccount<'info>, - /// SAFETY: #[account(mut)] request_queue: UncheckedAccount<'info>, - /// SAFETY: #[account(mut)] event_queue: UncheckedAccount<'info>, - /// SAFETY: #[account(mut)] bids: UncheckedAccount<'info>, - /// SAFETY: #[account(mut)] asks: UncheckedAccount<'info>, - /// SAFETY: // The `spl_token::Account` that funds will be taken from, i.e., transferred // from the user into the market's vault. // // For bids, this is the base currency. For asks, the quote. #[account(mut)] order_payer_token_account: UncheckedAccount<'info>, - /// SAFETY: // Also known as the "base" currency. For a given A/B market, // this is the vault for the A mint. #[account(mut)] coin_vault: UncheckedAccount<'info>, - /// SAFETY: // Also known as the "quote" currency. For a given A/B market, // this is the vault for the B mint. #[account(mut)] pc_vault: UncheckedAccount<'info>, - /// SAFETY: // PDA owner of the DEX's token accounts for base + quote currencies. vault_signer: UncheckedAccount<'info>, } @@ -652,7 +636,6 @@ pub struct DropStakeReward<'info> { not(feature = "test"), account(address = mint::SRM), )] - /// SAFETY: mint: UncheckedAccount<'info>, srm: DropStakeRewardPool<'info>, msrm: DropStakeRewardPool<'info>, @@ -669,14 +652,10 @@ pub struct DropStakeReward<'info> { // program to handle it. #[derive(Accounts)] pub struct DropStakeRewardPool<'info> { - /// SAFETY: registrar: UncheckedAccount<'info>, - /// SAFETY: reward_event_q: UncheckedAccount<'info>, pool_mint: Account<'info, Mint>, - /// SAFETY: vendor: UncheckedAccount<'info>, - /// SAFETY: vendor_vault: UncheckedAccount<'info>, } diff --git a/tests/chat/Anchor.toml b/tests/chat/Anchor.toml index 7d982c56dd..1249c7228d 100644 --- a/tests/chat/Anchor.toml +++ b/tests/chat/Anchor.toml @@ -7,3 +7,6 @@ chat = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/custom-coder/Anchor.toml b/tests/custom-coder/Anchor.toml index 589023aa22..ff7a52de2d 100644 --- a/tests/custom-coder/Anchor.toml +++ b/tests/custom-coder/Anchor.toml @@ -11,3 +11,6 @@ wallet = "~/.config/solana/id.json" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" + +[features] +safety_checks = false diff --git a/tests/errors/Anchor.toml b/tests/errors/Anchor.toml index a84e48eb42..52f2897d04 100644 --- a/tests/errors/Anchor.toml +++ b/tests/errors/Anchor.toml @@ -7,3 +7,6 @@ errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/escrow/Anchor.toml b/tests/escrow/Anchor.toml index 6639e2a570..f5dab7ec67 100644 --- a/tests/escrow/Anchor.toml +++ b/tests/escrow/Anchor.toml @@ -7,3 +7,6 @@ escrow = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run ts-mocha -t 1000000 tests/*.ts" + +[features] +safety_checks = false diff --git a/tests/ido-pool/Anchor.toml b/tests/ido-pool/Anchor.toml index 9c3c33bd9c..bb19294358 100644 --- a/tests/ido-pool/Anchor.toml +++ b/tests/ido-pool/Anchor.toml @@ -7,3 +7,6 @@ ido_pool = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/interface/Anchor.toml b/tests/interface/Anchor.toml index be62966b83..f77ab31faf 100644 --- a/tests/interface/Anchor.toml +++ b/tests/interface/Anchor.toml @@ -8,3 +8,6 @@ counter_auth = "Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/lockup/Anchor.toml b/tests/lockup/Anchor.toml index 04a6a65d58..4367e1dcc1 100644 --- a/tests/lockup/Anchor.toml +++ b/tests/lockup/Anchor.toml @@ -8,3 +8,6 @@ registry = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/pda-derivation/Anchor.toml b/tests/pda-derivation/Anchor.toml index 7893565164..2b17356f2c 100644 --- a/tests/pda-derivation/Anchor.toml +++ b/tests/pda-derivation/Anchor.toml @@ -1,5 +1,6 @@ [features] seeds = true +safety_checks = false [provider] cluster = "localnet" diff --git a/tests/pyth/Anchor.toml b/tests/pyth/Anchor.toml index 8407b65ba9..333e47210b 100644 --- a/tests/pyth/Anchor.toml +++ b/tests/pyth/Anchor.toml @@ -7,3 +7,6 @@ pyth = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" + +[features] +safety_checks = false diff --git a/tests/swap/Anchor.toml b/tests/swap/Anchor.toml index 0d8eff623a..993c8f1a46 100644 --- a/tests/swap/Anchor.toml +++ b/tests/swap/Anchor.toml @@ -11,3 +11,6 @@ program = "./deps/serum-dex/dex/target/deploy/serum_dex.so" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false diff --git a/tests/zero-copy/Anchor.toml b/tests/zero-copy/Anchor.toml index 385c550c3e..a2c3623277 100644 --- a/tests/zero-copy/Anchor.toml +++ b/tests/zero-copy/Anchor.toml @@ -11,3 +11,6 @@ test = "yarn run mocha -t 1000000 tests/" [programs.localnet] zero_cpi = "ErjUjtqKE5AGWUsjseSJCVLtddM6rhaMbDqmhzraF9h6" zero_copy = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" + +[features] +safety_checks = false From 1ddc90d845ec3a1e1c67e288604b774f36cd205e Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 20:26:28 +1300 Subject: [PATCH 21/30] Disable for cashiers-check --- tests/cashiers-check/Anchor.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/cashiers-check/Anchor.toml b/tests/cashiers-check/Anchor.toml index 4a7367c059..6b39d2572f 100644 --- a/tests/cashiers-check/Anchor.toml +++ b/tests/cashiers-check/Anchor.toml @@ -7,3 +7,6 @@ cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false From 729b73a51a6c7144058ad7807e01858cebbc16e1 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Thu, 17 Feb 2022 21:43:27 +1300 Subject: [PATCH 22/30] Update link --- lang/syn/src/parser/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index c0d736fb0b..6700b4729c 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -75,7 +75,7 @@ impl CrateContext { {}:{}:{} Struct field "{}" is unsafe, but is not documented. Please add a `/// SAFETY:` doc comment explaining why no checks through types are necessary. - See https://book.anchor-lang.com/chapter_3/errors.html#safety-checks for more information. + See https://book.anchor-lang.com/chapter_3/the_accounts_struct.html#safety-checks for more information. "#, ctx.file.canonicalize().unwrap().display(), span.start().line, From 8aee0c9f7695e790466493e9cb92e102808fd057 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 08:48:19 +1300 Subject: [PATCH 23/30] Remove SAFETY and leave only CHECK --- lang/syn/src/parser/context.rs | 6 ++-- tests/misc/programs/misc/src/context.rs | 48 ++++++++++++------------- tests/misc/programs/misc2/src/lib.rs | 2 +- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 6700b4729c..7a8bc10b30 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -50,9 +50,7 @@ impl CrateContext { let is_documented_or_safe = unsafe_field.attrs.iter().any(|attr| { attr.tokens.clone().into_iter().any(|token| match token { // Check for comments containing SAFETY: or CHECK: - proc_macro2::TokenTree::Literal(s) => { - s.to_string().contains("SAFETY:") || s.to_string().contains("CHECK:") - } + proc_macro2::TokenTree::Literal(s) => s.to_string().contains("CHECK"), proc_macro2::TokenTree::Group(g) => { g.stream().into_iter().any(|token| match token { proc_macro2::TokenTree::Ident(s) => { @@ -74,7 +72,7 @@ impl CrateContext { r#" {}:{}:{} Struct field "{}" is unsafe, but is not documented. - Please add a `/// SAFETY:` doc comment explaining why no checks through types are necessary. + Please add a `/// CHECK:` doc comment explaining why no checks through types are necessary. See https://book.anchor-lang.com/chapter_3/the_accounts_struct.html#safety-checks for more information. "#, ctx.file.canonicalize().unwrap().display(), diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index 25f7c9b375..da7f2125c6 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -28,7 +28,7 @@ pub struct TestTokenSeedsInit<'info> { )] pub my_pda: Account<'info, TokenAccount>, #[account(mut)] - /// SAFETY: + /// CHECK: pub authority: AccountInfo<'info>, pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, @@ -61,7 +61,7 @@ pub struct TestValidateAssociatedToken<'info> { )] pub token: Account<'info, TokenAccount>, pub mint: Account<'info, Mint>, - /// SAFETY: + /// CHECK: pub wallet: AccountInfo<'info>, } @@ -73,7 +73,7 @@ pub struct TestInstructionConstraint<'info> { bump = nonce, )] pub my_pda: AccountInfo<'info>, - /// SAFETY: + /// CHECK: pub my_account: AccountInfo<'info>, } @@ -89,7 +89,7 @@ pub struct TestPdaInit<'info> { pub my_pda: Account<'info, DataU16>, #[account(mut)] pub my_payer: Signer<'info>, - /// SAFETY: + /// CHECK: pub foo: AccountInfo<'info>, pub system_program: Program<'info, System>, } @@ -116,7 +116,7 @@ pub struct TestPdaMutZeroCopy<'info> { bump = my_pda.load()?.bump, )] pub my_pda: Loader<'info, DataZeroCopy>, - /// SAFETY: + /// CHECK: pub my_payer: AccountInfo<'info>, } @@ -140,35 +140,35 @@ pub struct InitializeSkipRentExempt<'info> { #[derive(Accounts)] pub struct InitializeNoRentExempt<'info> { - /// SAFETY: + /// CHECK: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestOwner<'info> { #[account(owner = *misc.key)] - /// SAFETY: + /// CHECK: pub data: AccountInfo<'info>, - /// SAFETY: + /// CHECK: pub misc: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestExecutable<'info> { #[account(executable)] - /// SAFETY: + /// CHECK: pub program: AccountInfo<'info>, } #[derive(Accounts)] pub struct TestStateCpi<'info> { #[account(signer)] - /// SAFETY: + /// CHECK: pub authority: AccountInfo<'info>, #[account(mut, state = misc2_program)] pub cpi_state: CpiState<'info, Misc2State>, #[account(executable)] - /// SAFETY: + /// CHECK: pub misc2_program: AccountInfo<'info>, } @@ -176,7 +176,7 @@ pub struct TestStateCpi<'info> { pub struct TestClose<'info> { #[account(mut, close = sol_dest)] pub data: Account<'info, Data>, - /// SAFETY: + /// CHECK: sol_dest: AccountInfo<'info>, } @@ -271,7 +271,7 @@ pub struct TestInitWithEmptySeeds<'info> { #[derive(Accounts)] pub struct TestEmptySeedsConstraint<'info> { #[account(seeds = [], bump)] - /// SAFETY: + /// CHECK: pub pda: AccountInfo<'info>, } @@ -300,7 +300,7 @@ pub struct TestInitIfNeededChecksOwner<'info> { #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>, - /// SAFETY: + /// CHECK: pub owner: AccountInfo<'info>, } @@ -324,9 +324,9 @@ pub struct TestInitMintIfNeeded<'info> { pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, - /// SAFETY: + /// CHECK: pub mint_authority: AccountInfo<'info>, - /// SAFETY: + /// CHECK: pub freeze_authority: AccountInfo<'info>, } @@ -340,7 +340,7 @@ pub struct TestInitTokenIfNeeded<'info> { pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, - /// SAFETY: + /// CHECK: pub authority: AccountInfo<'info>, } @@ -360,7 +360,7 @@ pub struct TestInitAssociatedTokenIfNeeded<'info> { pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, pub associated_token_program: Program<'info, AssociatedToken>, - /// SAFETY: + /// CHECK: pub authority: AccountInfo<'info>, } @@ -378,14 +378,14 @@ pub struct TestConstArraySize<'info> { #[derive(Accounts)] pub struct NoRentExempt<'info> { - /// SAFETY: + /// CHECK: pub data: AccountInfo<'info>, } #[derive(Accounts)] pub struct EnforceRentExempt<'info> { #[account(rent_exempt = enforce)] - /// SAFETY: + /// CHECK: pub data: AccountInfo<'info>, } @@ -401,7 +401,7 @@ pub struct InitDecreaseLamports<'info> { #[derive(Accounts)] pub struct InitIfNeededChecksRentExemption<'info> { #[account(init_if_needed, payer = user, space = 1000)] - /// SAFETY: + /// CHECK: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, @@ -434,17 +434,17 @@ pub struct TestProgramIdConstraintUsingFindPda<'info> { #[derive(Accounts)] pub struct TestUnsafeFieldSafetyErrors<'info> { #[doc = "test"] - /// SAFETY: + /// CHECK: pub data: UncheckedAccount<'info>, #[account(mut)] - /// SAFETY: + /// CHECK: pub data_two: UncheckedAccount<'info>, #[account( seeds = [b"my-seed", signer.key.as_ref()], bump )] pub data_three: UncheckedAccount<'info>, - /// SAFETY: + /// CHECK: pub data_four: UncheckedAccount<'info>, pub signer: Signer<'info>, pub system_program: Program<'info, System>, diff --git a/tests/misc/programs/misc2/src/lib.rs b/tests/misc/programs/misc2/src/lib.rs index 77e5029295..4ee2c9ae71 100644 --- a/tests/misc/programs/misc2/src/lib.rs +++ b/tests/misc/programs/misc2/src/lib.rs @@ -33,6 +33,6 @@ pub mod misc2 { #[derive(Accounts)] pub struct Auth<'info> { #[account(signer)] - /// SAFETY: + /// CHECK: pub authority: AccountInfo<'info>, } From 2e3b1d1605a9e99bc22e94f3f0757f73dceb7287 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 09:02:31 +1300 Subject: [PATCH 24/30] Update CFO submodules --- tests/cfo/deps/stake | 2 +- tests/cfo/deps/swap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cfo/deps/stake b/tests/cfo/deps/stake index f04b2aaf88..990eaa7944 160000 --- a/tests/cfo/deps/stake +++ b/tests/cfo/deps/stake @@ -1 +1 @@ -Subproject commit f04b2aaf8817dac4c8d89e75eaaa0c099dfbf166 +Subproject commit 990eaa7944c6682838fdaa6c14cc07ed680007f0 diff --git a/tests/cfo/deps/swap b/tests/cfo/deps/swap index b3021f1444..96e3b1e2a5 160000 --- a/tests/cfo/deps/swap +++ b/tests/cfo/deps/swap @@ -1 +1 @@ -Subproject commit b3021f1444280a372721133bb2e5acca2d271283 +Subproject commit 96e3b1e2a53a95ef56e6ec2da68348ffd6a5c091 From dc5e0d9fffa0f9a50dfc4aea8c64edf6d190dcf9 Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 09:10:39 +1300 Subject: [PATCH 25/30] Remove init and seeds cases --- lang/syn/src/parser/context.rs | 16 +++------------- tests/misc/programs/misc/src/context.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs index 7a8bc10b30..1a924bd48d 100644 --- a/lang/syn/src/parser/context.rs +++ b/lang/syn/src/parser/context.rs @@ -47,24 +47,14 @@ impl CrateContext { for (_, ctx) in self.modules.iter() { for unsafe_field in ctx.unsafe_struct_fields() { // Check if unsafe field type has been documented with a /// SAFETY: doc string. - let is_documented_or_safe = unsafe_field.attrs.iter().any(|attr| { + let is_documented = unsafe_field.attrs.iter().any(|attr| { attr.tokens.clone().into_iter().any(|token| match token { - // Check for comments containing SAFETY: or CHECK: + // Check for doc comments containing CHECK proc_macro2::TokenTree::Literal(s) => s.to_string().contains("CHECK"), - proc_macro2::TokenTree::Group(g) => { - g.stream().into_iter().any(|token| match token { - proc_macro2::TokenTree::Ident(s) => { - // Allow PDAs and accounts which are being initialized without - // doc comments - s == "seeds" || s == "init" - } - _ => false, - }) - } _ => false, }) }); - if !is_documented_or_safe { + if !is_documented { let ident = unsafe_field.ident.as_ref().unwrap(); let span = ident.span(); // Error if undocumented. diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index da7f2125c6..cda496cbe8 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -72,6 +72,7 @@ pub struct TestInstructionConstraint<'info> { seeds = [b"my-seed", my_account.key.as_ref()], bump = nonce, )] + /// CHECK: pub my_pda: AccountInfo<'info>, /// CHECK: pub my_account: AccountInfo<'info>, @@ -296,6 +297,7 @@ pub struct TestInitIfNeeded<'info> { #[derive(Accounts)] pub struct TestInitIfNeededChecksOwner<'info> { #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] + /// CHECK: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -308,6 +310,7 @@ pub struct TestInitIfNeededChecksOwner<'info> { #[instruction(seed_data: String)] pub struct TestInitIfNeededChecksSeeds<'info> { #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] + /// CHECK: pub data: UncheckedAccount<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -392,6 +395,7 @@ pub struct EnforceRentExempt<'info> { #[derive(Accounts)] pub struct InitDecreaseLamports<'info> { #[account(init, payer = user, space = 1000)] + /// CHECK: pub data: AccountInfo<'info>, #[account(mut)] pub user: Signer<'info>, @@ -414,9 +418,11 @@ pub struct TestProgramIdConstraint<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] + /// CHECK: second: AccountInfo<'info>, } @@ -425,9 +431,11 @@ pub struct TestProgramIdConstraintUsingFindPda<'info> { // not a real associated token account // just deriving like this for testing purposes #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: first: AccountInfo<'info>, #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] + /// CHECK: second: AccountInfo<'info>, } @@ -443,6 +451,7 @@ pub struct TestUnsafeFieldSafetyErrors<'info> { seeds = [b"my-seed", signer.key.as_ref()], bump )] + /// CHECK: pub data_three: UncheckedAccount<'info>, /// CHECK: pub data_four: UncheckedAccount<'info>, From ff7fc25fd72171f169de8c5f7a6a7f7c21facc6d Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 09:29:11 +1300 Subject: [PATCH 26/30] Disable for multisig --- tests/multisig/Anchor.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/multisig/Anchor.toml b/tests/multisig/Anchor.toml index e76d60f889..f8bc62e0d5 100644 --- a/tests/multisig/Anchor.toml +++ b/tests/multisig/Anchor.toml @@ -7,3 +7,6 @@ multisig = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false From ff0104e583af3157266e75c0538d2c8afa94607e Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 09:46:29 +1300 Subject: [PATCH 27/30] Disable for spl/token-proxy --- tests/spl/token-proxy/Anchor.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/spl/token-proxy/Anchor.toml b/tests/spl/token-proxy/Anchor.toml index 5c5f278453..fea8211808 100644 --- a/tests/spl/token-proxy/Anchor.toml +++ b/tests/spl/token-proxy/Anchor.toml @@ -7,3 +7,6 @@ token_proxy = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" [scripts] test = "yarn run mocha -t 1000000 tests/" + +[features] +safety_checks = false From 582231c7b0a1e599d25811b9306bb57267620a4a Mon Sep 17 00:00:00 2001 From: Tom Linton Date: Fri, 18 Feb 2022 10:13:51 +1300 Subject: [PATCH 28/30] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f64a2f3b54..72aaab98b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ incremented for features. ### Breaking * lang: Enforce that the payer for an init-ed account be marked `mut` ([#1271](https://github.com/project-serum/anchor/pull/1271)). +* lang: Require doc comments when using AccountInfo or UncheckedAccount types ([#1452](https://github.com/project-serum/anchor/pull/1452)). ## [0.21.0] - 2022-02-07 From 0082e0fec0ee2f552a7e156d9fe13f47f5b4a620 Mon Sep 17 00:00:00 2001 From: Armani Ferrante Date: Thu, 17 Feb 2022 17:11:43 -0500 Subject: [PATCH 29/30] add integratoin test --- .github/workflows/tests.yaml | 2 ++ tests/safety-checks/.gitignore | 6 +++++ tests/safety-checks/Anchor.toml | 13 +++++++++ tests/safety-checks/Cargo.toml | 4 +++ tests/safety-checks/migrations/deploy.ts | 12 +++++++++ .../programs/account-info/Cargo.toml | 19 +++++++++++++ .../programs/account-info/Xargo.toml | 2 ++ .../programs/account-info/src/lib.rs | 16 +++++++++++ .../programs/unchecked-account/Cargo.toml | 19 +++++++++++++ .../programs/unchecked-account/Xargo.toml | 2 ++ .../programs/unchecked-account/src/lib.rs | 16 +++++++++++ tests/safety-checks/test.sh | 27 +++++++++++++++++++ tests/safety-checks/tests/safety-checks.ts | 17 ++++++++++++ tests/safety-checks/tsconfig.json | 10 +++++++ 14 files changed, 165 insertions(+) create mode 100644 tests/safety-checks/.gitignore create mode 100644 tests/safety-checks/Anchor.toml create mode 100644 tests/safety-checks/Cargo.toml create mode 100644 tests/safety-checks/migrations/deploy.ts create mode 100644 tests/safety-checks/programs/account-info/Cargo.toml create mode 100644 tests/safety-checks/programs/account-info/Xargo.toml create mode 100644 tests/safety-checks/programs/account-info/src/lib.rs create mode 100644 tests/safety-checks/programs/unchecked-account/Cargo.toml create mode 100644 tests/safety-checks/programs/unchecked-account/Xargo.toml create mode 100644 tests/safety-checks/programs/unchecked-account/src/lib.rs create mode 100755 tests/safety-checks/test.sh create mode 100644 tests/safety-checks/tests/safety-checks.ts create mode 100644 tests/safety-checks/tsconfig.json diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index d1b4fb1960..9dd20afb6d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -278,6 +278,8 @@ jobs: path: tests/auction-house - cmd: cd tests/floats && yarn && anchor test path: tests/floats + - cmd: cd tests/safety-checks && ./test.sh + path: tests/safety-checks steps: - uses: actions/checkout@v2 - uses: ./.github/actions/setup/ diff --git a/tests/safety-checks/.gitignore b/tests/safety-checks/.gitignore new file mode 100644 index 0000000000..51448d4dab --- /dev/null +++ b/tests/safety-checks/.gitignore @@ -0,0 +1,6 @@ + +.anchor +.DS_Store +target +**/*.rs.bk +node_modules diff --git a/tests/safety-checks/Anchor.toml b/tests/safety-checks/Anchor.toml new file mode 100644 index 0000000000..778993edc0 --- /dev/null +++ b/tests/safety-checks/Anchor.toml @@ -0,0 +1,13 @@ +[programs.localnet] +unchecked_account = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" +account_info = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" + +[registry] +url = "https://anchor.projectserum.com" + +[provider] +cluster = "localnet" +wallet = "/home/armaniferrante/.config/solana/id.json" + +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/safety-checks/Cargo.toml b/tests/safety-checks/Cargo.toml new file mode 100644 index 0000000000..a60de986d3 --- /dev/null +++ b/tests/safety-checks/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "programs/*" +] diff --git a/tests/safety-checks/migrations/deploy.ts b/tests/safety-checks/migrations/deploy.ts new file mode 100644 index 0000000000..325cf3d0ec --- /dev/null +++ b/tests/safety-checks/migrations/deploy.ts @@ -0,0 +1,12 @@ +// Migrations are an early feature. Currently, they're nothing more than this +// single deploy script that's invoked from the CLI, injecting a provider +// configured from the workspace's Anchor.toml. + +const anchor = require("@project-serum/anchor"); + +module.exports = async function (provider) { + // Configure client to use the provider. + anchor.setProvider(provider); + + // Add your deploy script here. +} diff --git a/tests/safety-checks/programs/account-info/Cargo.toml b/tests/safety-checks/programs/account-info/Cargo.toml new file mode 100644 index 0000000000..032a72274b --- /dev/null +++ b/tests/safety-checks/programs/account-info/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "account-info" +version = "0.1.0" +description = "Created with Anchor" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "account_info" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.21.0" diff --git a/tests/safety-checks/programs/account-info/Xargo.toml b/tests/safety-checks/programs/account-info/Xargo.toml new file mode 100644 index 0000000000..475fb71ed1 --- /dev/null +++ b/tests/safety-checks/programs/account-info/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/tests/safety-checks/programs/account-info/src/lib.rs b/tests/safety-checks/programs/account-info/src/lib.rs new file mode 100644 index 0000000000..670e10738f --- /dev/null +++ b/tests/safety-checks/programs/account-info/src/lib.rs @@ -0,0 +1,16 @@ +use anchor_lang::prelude::*; + +declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); + +#[program] +pub mod account_info { + use super::*; + pub fn initialize(ctx: Context) -> ProgramResult { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Initialize<'info> { + unchecked: AccountInfo<'info>, +} diff --git a/tests/safety-checks/programs/unchecked-account/Cargo.toml b/tests/safety-checks/programs/unchecked-account/Cargo.toml new file mode 100644 index 0000000000..69443051c2 --- /dev/null +++ b/tests/safety-checks/programs/unchecked-account/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "unchecked-account" +version = "0.1.0" +description = "Created with Anchor" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "safety_checks" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = { path = "../../../../lang" } diff --git a/tests/safety-checks/programs/unchecked-account/Xargo.toml b/tests/safety-checks/programs/unchecked-account/Xargo.toml new file mode 100644 index 0000000000..475fb71ed1 --- /dev/null +++ b/tests/safety-checks/programs/unchecked-account/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/tests/safety-checks/programs/unchecked-account/src/lib.rs b/tests/safety-checks/programs/unchecked-account/src/lib.rs new file mode 100644 index 0000000000..4d86648db0 --- /dev/null +++ b/tests/safety-checks/programs/unchecked-account/src/lib.rs @@ -0,0 +1,16 @@ +use anchor_lang::prelude::*; + +declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); + +#[program] +pub mod unchecked_account { + use super::*; + pub fn initialize(ctx: Context) -> ProgramResult { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Initialize<'info> { + unchecked: UncheckedAccount<'info>, +} diff --git a/tests/safety-checks/test.sh b/tests/safety-checks/test.sh new file mode 100755 index 0000000000..d9d4cec5da --- /dev/null +++ b/tests/safety-checks/test.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +echo "Building programs" + +# +# Build the UncheckedAccount variant. +# +pushd programs/unchecked-account/ +anchor build +if [ $? -eq 0 ]; then + echo "Error: expected failure" + exit 1 +fi +popd + +# +# Build the AccountInfo variant. +# +pushd programs/account-info/ +anchor build +if [ $? -eq 0 ]; then + echo "Error: expected failure" + exit 1 +fi +popd + +echo "Success. All builds failed." diff --git a/tests/safety-checks/tests/safety-checks.ts b/tests/safety-checks/tests/safety-checks.ts new file mode 100644 index 0000000000..5cdbc43e5c --- /dev/null +++ b/tests/safety-checks/tests/safety-checks.ts @@ -0,0 +1,17 @@ +import * as anchor from '@project-serum/anchor'; +import { Program } from '@project-serum/anchor'; +import { SafetyChecks } from '../target/types/safety_checks'; + +describe('safety-checks', () => { + + // Configure the client to use the local cluster. + anchor.setProvider(anchor.Provider.env()); + + const program = anchor.workspace.SafetyChecks as Program; + + it('Is initialized!', async () => { + // Add your test here. + const tx = await program.rpc.initialize({}); + console.log("Your transaction signature", tx); + }); +}); diff --git a/tests/safety-checks/tsconfig.json b/tests/safety-checks/tsconfig.json new file mode 100644 index 0000000000..cd5d2e3d06 --- /dev/null +++ b/tests/safety-checks/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } +} From ed50c6205d1a85b697ec27457550957e471d33c9 Mon Sep 17 00:00:00 2001 From: Armani Ferrante Date: Thu, 17 Feb 2022 17:25:03 -0500 Subject: [PATCH 30/30] lint --- tests/safety-checks/migrations/deploy.ts | 2 +- tests/safety-checks/tests/safety-checks.ts | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/safety-checks/migrations/deploy.ts b/tests/safety-checks/migrations/deploy.ts index 325cf3d0ec..5e3df0dc30 100644 --- a/tests/safety-checks/migrations/deploy.ts +++ b/tests/safety-checks/migrations/deploy.ts @@ -9,4 +9,4 @@ module.exports = async function (provider) { anchor.setProvider(provider); // Add your deploy script here. -} +}; diff --git a/tests/safety-checks/tests/safety-checks.ts b/tests/safety-checks/tests/safety-checks.ts index 5cdbc43e5c..2eed33f1b8 100644 --- a/tests/safety-checks/tests/safety-checks.ts +++ b/tests/safety-checks/tests/safety-checks.ts @@ -1,15 +1,14 @@ -import * as anchor from '@project-serum/anchor'; -import { Program } from '@project-serum/anchor'; -import { SafetyChecks } from '../target/types/safety_checks'; - -describe('safety-checks', () => { +import * as anchor from "@project-serum/anchor"; +import { Program } from "@project-serum/anchor"; +import { SafetyChecks } from "../target/types/safety_checks"; +describe("safety-checks", () => { // Configure the client to use the local cluster. anchor.setProvider(anchor.Provider.env()); const program = anchor.workspace.SafetyChecks as Program; - it('Is initialized!', async () => { + it("Is initialized!", async () => { // Add your test here. const tx = await program.rpc.initialize({}); console.log("Your transaction signature", tx);