diff --git a/404.html b/404.html index 0d1af8a5..6d3627f0 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 01f9d412..32434930 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, Stellar, 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 6e4364ea..5467a515 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 the Web3 Foundation Grants Program, the Aleph Zero Ecosystem Funding Program and the Stellar Community Fund .

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.
Stellar Community FundWe added support for Stellar's smart contract language, Soroban. We included various output formats, such as an HTML report, improved the tool's precision and recall, and added a GitHub action to run the tool with pull requests.
- + \ No newline at end of file diff --git a/assets/js/23c01a81.c1803f90.js b/assets/js/23c01a81.c1803f90.js new file mode 100644 index 00000000..4512ade3 --- /dev/null +++ b/assets/js/23c01a81.c1803f90.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[6337],{9613:(e,t,i)=>{i.d(t,{Zo:()=>d,kt:()=>f});var r=i(9496);function n(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function o(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,r)}return i}function a(e){for(var t=1;t=0||(n[i]=e[i]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,i)&&(n[i]=e[i])}return n}var s=r.createContext({}),p=function(e){var t=r.useContext(s),i=t;return e&&(i="function"==typeof e?e(t):a(a({},t),e)),i},d=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},c="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var i=e.components,n=e.mdxType,o=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(i),m=n,f=c["".concat(s,".").concat(m)]||c[m]||u[m]||o;return i?r.createElement(f,a(a({ref:t},d),{},{components:i})):r.createElement(f,a({ref:t},d))}));function f(e,t){var i=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=i.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[c]="string"==typeof e?e:n,a[1]=l;for(var p=2;p{i.r(t),i.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var r=i(2564),n=(i(9496),i(9613));const o={},a="Divide before multiply",l={unversionedId:"detectors/divide-before-multiply",id:"detectors/divide-before-multiply",title:"Divide before multiply",description:"Description",source:"@site/docs/detectors/1-divide-before-multiply.md",sourceDirName:"detectors",slug:"/detectors/divide-before-multiply",permalink:"/scout-soroban/docs/detectors/divide-before-multiply",draft:!1,editUrl:"https://github.com/CoinFabrik/scout-soroban/docs/detectors/1-divide-before-multiply.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Detectors",permalink:"/scout-soroban/docs/detectors/"},next:{title:"Unsafe unwrap",permalink:"/scout-soroban/docs/detectors/unsafe-unwrap"}},s={},p=[{value:"Description",id:"description",level:2},{value:"Why is this bad?",id:"why-is-this-bad",level:2},{value:"Issue example",id:"issue-example",level:2},{value:"Remediated example",id:"remediated-example",level:2},{value:"How is it detected?",id:"how-is-it-detected",level:2},{value:"References",id:"references",level:2}],d={toc:p},c="wrapper";function u(e){let{components:t,...i}=e;return(0,n.kt)(c,(0,r.Z)({},d,i,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"divide-before-multiply"},"Divide before multiply"),(0,n.kt)("h2",{id:"description"},"Description"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"Category: ",(0,n.kt)("inlineCode",{parentName:"li"},"Arithmetic")),(0,n.kt)("li",{parentName:"ul"},"Severity: ",(0,n.kt)("inlineCode",{parentName:"li"},"Medium")),(0,n.kt)("li",{parentName:"ul"},"Detectors: ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/divide-before-multiply"},(0,n.kt)("inlineCode",{parentName:"a"},"divide-before-multiply"))),(0,n.kt)("li",{parentName:"ul"},"Test Cases: ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/divide-before-multiply/divide-before-multiply-1"},(0,n.kt)("inlineCode",{parentName:"a"},"divide-before-multiply-1"))," ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/divide-before-multiply/divide-before-multiply-2"},(0,n.kt)("inlineCode",{parentName:"a"},"divide-before-multiply-2"))," ",(0,n.kt)("a",{parentName:"li",href:"https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/divide-before-multiply/divide-before-multiply-3"},(0,n.kt)("inlineCode",{parentName:"a"},"divide-before-multiply-3")))),(0,n.kt)("p",null,"In Rust, the order of operations can influence the precision of the result, especially in integer arithmetic. "),(0,n.kt)("h2",{id:"why-is-this-bad"},"Why is this bad?"),(0,n.kt)("p",null,"Performing a division operation before a multiplication can lead to a loss of precision as division between integers might return zero. "),(0,n.kt)("h2",{id:"issue-example"},"Issue example"),(0,n.kt)("p",null,"Consider the following ",(0,n.kt)("inlineCode",{parentName:"p"},"Soroban")," contract:"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},"\n pub fn split_profit(percentage: u64, total_profit: u64) -> u64 {\n (percentage / 100) * total_profit\n}\n\n")),(0,n.kt)("p",null,"In this contract, the ",(0,n.kt)("inlineCode",{parentName:"p"},"split_profit")," function divides the ",(0,n.kt)("inlineCode",{parentName:"p"},"percentage")," by ",(0,n.kt)("inlineCode",{parentName:"p"},"100")," before multiplying it with ",(0,n.kt)("inlineCode",{parentName:"p"},"total_profit"),". This could lead to a loss of precision if ",(0,n.kt)("inlineCode",{parentName:"p"},"percentage")," is less than ",(0,n.kt)("inlineCode",{parentName:"p"},"100")," as the division would return ",(0,n.kt)("inlineCode",{parentName:"p"},"0"),". This could lead to incorrect calculations and potential financial loss in a real-world smart contract."),(0,n.kt)("p",null,"The code example can be found ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/divide-before-multiply/divide-before-multiply-1/vulnerable-example"},"here"),"."),(0,n.kt)("h2",{id:"remediated-example"},"Remediated example"),(0,n.kt)("p",null,"Reverse the order of operations to ensure multiplication occurs before division."),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-rust"},"\n pub fn split_profit(&self, percentage: u64, total_profit: u64) -> u64 {\n (percentage * total_profit) / 100\n }\n \n")),(0,n.kt)("p",null,"The remediated code example can be found ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/divide-before-multiply/divide-before-multiply-1/remediated-example"},"here"),"."),(0,n.kt)("h2",{id:"how-is-it-detected"},"How is it detected?"),(0,n.kt)("p",null,"Checks the existence of a division before a multiplication."),(0,n.kt)("h2",{id:"references"},"References"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators"},"Rust documentation: ",(0,n.kt)("inlineCode",{parentName:"a"},"Integer Division"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/23c01a81.d5d3b4f8.js b/assets/js/23c01a81.d5d3b4f8.js deleted file mode 100644 index 152b5075..00000000 --- a/assets/js/23c01a81.d5d3b4f8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscout=self.webpackChunkscout||[]).push([[6337],{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 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 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,o=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=c(r),m=o,f=u["".concat(s,".").concat(m)]||u[m]||d[m]||i;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,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);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:o,a[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var n=r(2564),o=(r(9496),r(9613));const i={},a="Divide before multiply",l={unversionedId:"detectors/divide-before-multiply",id:"detectors/divide-before-multiply",title:"Divide before multiply",description:"What it does",source:"@site/docs/detectors/1-divide-before-multiply.md",sourceDirName:"detectors",slug:"/detectors/divide-before-multiply",permalink:"/scout-soroban/docs/detectors/divide-before-multiply",draft:!1,editUrl:"https://github.com/CoinFabrik/scout-soroban/docs/detectors/1-divide-before-multiply.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"docsSidebar",previous:{title:"Detectors",permalink:"/scout-soroban/docs/detectors/"},next:{title:"Unsafe unwrap",permalink:"/scout-soroban/docs/detectors/unsafe-unwrap"}},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:"Example",id:"example",level:3},{value:"Implementation",id:"implementation",level:3}],p={toc:c},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:"divide-before-multiply"},"Divide before multiply"),(0,o.kt)("h3",{id:"what-it-does"},"What it does"),(0,o.kt)("p",null,"Checks the existence of a division before a multiplication."),(0,o.kt)("h3",{id:"why-is-this-bad"},"Why is this bad?"),(0,o.kt)("p",null,"Performing a division operation before multiplication can lead to a loss of precision. It might even result in an unintended zero value."),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"// Example 1 - Vulnerable\nlet x = 10;\nlet y = 6;\nlet z = x / y * 3; // z evaluates to 3\n\n// Example 2 - Vulnerable\nlet a = 1;\nlet b = 2;\nlet c = a / b * 3; // c evaluates to 0\n")),(0,o.kt)("p",null,"Use instead:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-rust"},"// Example 1 - Remediated\nlet x = 10;\nlet y = 6;\nlet z = x * 3 / y; // z evaluates to 5\n\n// Example 2 - Remediated\nlet a = 1;\nlet b = 2;\nlet c = a * 3 / b; // c evaluates to 1\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-soroban/tree/main/detectors/divide-before-multiply"},"this link"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.fdab817a.js b/assets/js/935f2afb.0ba531f6.js similarity index 52% rename from assets/js/935f2afb.fdab817a.js rename to assets/js/935f2afb.0ba531f6.js index 293a12bf..0afa4531 100644 --- a/assets/js/935f2afb.fdab817a.js +++ b/assets/js/935f2afb.0ba531f6.js @@ -1 +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-soroban/docs/intro","docId":"intro"},{"type":"category","label":"Vulnerabilities","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Divide before multiply","href":"/scout-soroban/docs/vulnerabilities/divide-before-multiply","docId":"vulnerabilities/divide-before-multiply"},{"type":"link","label":"Unsafe unwrap","href":"/scout-soroban/docs/vulnerabilities/unsafe-unwrap","docId":"vulnerabilities/unsafe-unwrap"},{"type":"link","label":"Unsafe expect","href":"/scout-soroban/docs/vulnerabilities/unsafe-expect","docId":"vulnerabilities/unsafe-expect"},{"type":"link","label":"Overflow check","href":"/scout-soroban/docs/vulnerabilities/overflow-check","docId":"vulnerabilities/overflow-check"},{"type":"link","label":"Insufficiently random values","href":"/scout-soroban/docs/vulnerabilities/insufficiently-random-values","docId":"vulnerabilities/insufficiently-random-values"},{"type":"link","label":"Unprotected update current contract wasm","href":"/scout-soroban/docs/vulnerabilities/unprotected-update-current-contract-wasm","docId":"vulnerabilities/unprotected-update-current-contract-wasm"},{"type":"link","label":"Avoid core::mem::forget usage","href":"/scout-soroban/docs/vulnerabilities/avoid-core-mem-forget","docId":"vulnerabilities/avoid-core-mem-forget"},{"type":"link","label":"Set contract storage","href":"/scout-soroban/docs/vulnerabilities/set-contract-storage","docId":"vulnerabilities/set-contract-storage"},{"type":"link","label":"Avoid panic error","href":"/scout-soroban/docs/vulnerabilities/avoid-panic-error","docId":"vulnerabilities/avoid-panic-error"},{"type":"link","label":"Avoid unsafe block","href":"/scout-soroban/docs/vulnerabilities/avoid-unsafe-block","docId":"vulnerabilities/avoid-unsafe-block"},{"type":"link","label":"DoS unbounded operation","href":"/scout-soroban/docs/vulnerabilities/dos-unbounded-operation","docId":"vulnerabilities/dos-unbounded-operation"},{"type":"link","label":"Soroban version","href":"/scout-soroban/docs/vulnerabilities/soroban-version","docId":"vulnerabilities/soroban-version"},{"type":"link","label":"Unused return enum","href":"/scout-soroban/docs/vulnerabilities/unused-return-enum","docId":"vulnerabilities/unused-return-enum"},{"type":"link","label":"Iterators over indexing","href":"/scout-soroban/docs/vulnerabilities/iterators-over-indexing","docId":"vulnerabilities/iterators-over-indexing"},{"type":"link","label":"Assert violation","href":"/scout-soroban/docs/vulnerabilities/assert-violation","docId":"vulnerabilities/assert-violation"},{"type":"link","label":"Unprotected mapping operation","href":"/scout-soroban/docs/vulnerabilities/unprotected-mapping-operation","docId":"vulnerabilities/unprotected-mapping-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout-soroban/docs/vulnerabilities/dos-unexpected-revert-with-vector","docId":"vulnerabilities/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout-soroban/docs/vulnerabilities/unrestricted-transfer-from","docId":"vulnerabilities/unrestricted-transfer-from"},{"type":"link","label":"Unsafe map get","href":"/scout-soroban/docs/vulnerabilities/unsafe-map-get","docId":"vulnerabilities/unsafe-map-get"},{"type":"link","label":"Incorrect Exponentiation","href":"/scout-soroban/docs/vulnerabilities/incorrect-exponentiation","docId":"vulnerabilities/incorrect-exponentiation"}],"href":"/scout-soroban/docs/vulnerabilities/"},{"type":"category","label":"Detectors","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Divide before multiply","href":"/scout-soroban/docs/detectors/divide-before-multiply","docId":"detectors/divide-before-multiply"},{"type":"link","label":"Unsafe unwrap","href":"/scout-soroban/docs/detectors/unsafe-unwrap","docId":"detectors/unsafe-unwrap"},{"type":"link","label":"Unsafe expect","href":"/scout-soroban/docs/detectors/unsafe-expect","docId":"detectors/unsafe-expect"},{"type":"link","label":"Overflow-check","href":"/scout-soroban/docs/detectors/overflow-check","docId":"detectors/overflow-check"},{"type":"link","label":"Insuficciently random values","href":"/scout-soroban/docs/detectors/insufficiently-random-values","docId":"detectors/insufficiently-random-values"},{"type":"link","label":"Unprotected update of current contract wasm","href":"/scout-soroban/docs/detectors/unprotected-update-current-contract-wasm","docId":"detectors/unprotected-update-current-contract-wasm"},{"type":"link","label":"Avoid core mem forget usage","href":"/scout-soroban/docs/detectors/avoid-core-mem-forget","docId":"detectors/avoid-core-mem-forget"},{"type":"link","label":"Set contract storage","href":"/scout-soroban/docs/detectors/set-contract-storage","docId":"detectors/set-contract-storage"},{"type":"link","label":"Panic error","href":"/scout-soroban/docs/detectors/avoid-panic-error","docId":"detectors/avoid-panic-error"},{"type":"link","label":"Avoid unsafe block","href":"/scout-soroban/docs/detectors/avoid-unsafe-block","docId":"detectors/avoid-unsafe-block"},{"type":"link","label":"DoS unbounded operation","href":"/scout-soroban/docs/detectors/dos-unbounded-operation","docId":"detectors/dos-unbounded-operation"},{"type":"link","label":"Soroban version","href":"/scout-soroban/docs/detectors/soroban-version","docId":"detectors/soroban-version"},{"type":"link","label":"Unused return enum","href":"/scout-soroban/docs/detectors/unused-return-enum","docId":"detectors/unused-return-enum"},{"type":"link","label":"Iterators-over-indexing","href":"/scout-soroban/docs/detectors/iterators-over-indexing","docId":"detectors/iterators-over-indexing"},{"type":"link","label":"Assert violation","href":"/scout-soroban/docs/detectors/assert-violation","docId":"detectors/assert-violation"},{"type":"link","label":"Unprotected Mapping Operation","href":"/scout-soroban/docs/detectors/unprotected-mapping-operation","docId":"detectors/unprotected-mapping-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout-soroban/docs/detectors/dos-unexpected-revert-with-vector","docId":"detectors/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout-soroban/docs/detectors/unrestricted-transfer-from","docId":"detectors/unrestricted-transfer-from"},{"type":"link","label":"Unsafe map get","href":"/scout-soroban/docs/detectors/unsafe-map-get","docId":"detectors/unsafe-map-get"},{"type":"link","label":"Zero or test address","href":"/scout-soroban/docs/detectors/zero-or-test-address","docId":"detectors/zero-or-test-address"},{"type":"link","label":"Incorrect Exponentiation","href":"/scout-soroban/docs/detectors/incorrect-exponentiation","docId":"detectors/incorrect-exponentiation"},{"type":"link","label":"Integer overflow or underflow","href":"/scout-soroban/docs/detectors/integer-overflow -or-underflow","docId":"detectors/integer-overflow -or-underflow"}],"href":"/scout-soroban/docs/detectors/"},{"type":"link","label":"Contribute","href":"/scout-soroban/docs/contribute","docId":"contribute"},{"type":"link","label":"Architecture","href":"/scout-soroban/docs/architecture","docId":"architecture"},{"type":"category","label":"Precision and recall","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Scout Bug Fighter for Soroban: Improving Tool\'s Precision","href":"/scout-soroban/docs/precision-and-recall/first-iteration","docId":"precision-and-recall/first-iteration"}],"href":"/scout-soroban/docs/precision-and-recall/"},{"type":"link","label":"Scout GitHub Action","href":"/scout-soroban/docs/github-action","docId":"github-action"},{"type":"link","label":"Scout VS Code Extension","href":"/scout-soroban/docs/vscode-extension","docId":"vscode-extension"},{"type":"link","label":"Scout Soroban Smart Contracts Examples","href":"/scout-soroban/docs/soroban-examples","docId":"soroban-examples"}]},"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\u200b","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-panic-error":{"id":"detectors/avoid-panic-error","title":"Panic error","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/divide-before-multiply":{"id":"detectors/divide-before-multiply","title":"Divide before multiply","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/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":"Description","sidebar":"docsSidebar"},"detectors/iterators-over-indexing":{"id":"detectors/iterators-over-indexing","title":"Iterators-over-indexing","description":"What it does","sidebar":"docsSidebar"},"detectors/overflow-check":{"id":"detectors/overflow-check","title":"Overflow-check","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/set-contract-storage":{"id":"detectors/set-contract-storage","title":"Set contract storage","description":"What it does","sidebar":"docsSidebar"},"detectors/soroban-version":{"id":"detectors/soroban-version","title":"Soroban version","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-update-current-contract-wasm":{"id":"detectors/unprotected-update-current-contract-wasm","title":"Unprotected update of current contract wasm","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-map-get":{"id":"detectors/unsafe-map-get","title":"Unsafe map get","description":"What it does","sidebar":"docsSidebar"},"detectors/unsafe-unwrap":{"id":"detectors/unsafe-unwrap","title":"Unsafe unwrap","description":"Description","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"},"github-action":{"id":"github-action","title":"Scout GitHub Action","description":"At CoinFabrik, we understand the importance of ensuring code quality and security in every step of the development process. That\'s why we\'ve developed a GitHub action to integrate Scout into the CI/CD pipeline.","sidebar":"docsSidebar"},"intro":{"id":"intro","title":"Getting Started","description":"Let\'s discover Scout in less than 5 minutes!.","sidebar":"docsSidebar"},"precision-and-recall/first-iteration":{"id":"precision-and-recall/first-iteration","title":"Scout Bug Fighter for Soroban: Improving Tool\'s Precision","description":"In the scope of the second grant awarded to CoinFabrik by the Stellar Community Fund to advance the development of Scout for Soroban, the focus extends beyond incorporating new detectors and refining features. A key objective of this grant is to subject the tool to rigorous testing against real Soroban projects. Through this process, the aim is to analyze the outcomes meticulously, identifying areas for enhancement to increase the tool\'s precision. This includes minimizing false positives and false negatives, thereby fortifying its efficacy.","sidebar":"docsSidebar"},"precision-and-recall/README":{"id":"precision-and-recall/README","title":"Precision and recall","description":"This section outlines the tasks we perform to enhance the overall quality of Scout.","sidebar":"docsSidebar"},"soroban-examples":{"id":"soroban-examples","title":"Scout Soroban Smart Contracts Examples","description":"In the context of Scout\'s development, we engaged developers without Soroban experience to create a series of smart contracts within tight time constraints, encouraging them to introduce errors.","sidebar":"docsSidebar"},"vscode-extension":{"id":"vscode-extension","title":"Scout VS Code Extension","description":"Add Scout to your development workspace with Scout\'s VS Code extension and run Scout automatically upon saving your file.","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-panic-error":{"id":"vulnerabilities/avoid-panic-error","title":"Avoid panic error","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-unsafe-block":{"id":"vulnerabilities/avoid-unsafe-block","title":"Avoid unsafe block","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/divide-before-multiply":{"id":"vulnerabilities/divide-before-multiply","title":"Divide before multiply","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/insufficiently-random-values":{"id":"vulnerabilities/insufficiently-random-values","title":"Insufficiently random values","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/iterators-over-indexing":{"id":"vulnerabilities/iterators-over-indexing","title":"Iterators over indexing","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/overflow-check":{"id":"vulnerabilities/overflow-check","title":"Overflow check","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. 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/set-contract-storage":{"id":"vulnerabilities/set-contract-storage","title":"Set contract storage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/soroban-version":{"id":"vulnerabilities/soroban-version","title":"Soroban version","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-mapping-operation":{"id":"vulnerabilities/unprotected-mapping-operation","title":"Unprotected mapping operation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-update-current-contract-wasm":{"id":"vulnerabilities/unprotected-update-current-contract-wasm","title":"Unprotected update current contract wasm","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-map-get":{"id":"vulnerabilities/unsafe-map-get","title":"Unsafe map get","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"}}}')}}]); \ No newline at end of file +"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-soroban/docs/intro","docId":"intro"},{"type":"category","label":"Vulnerabilities","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Divide before multiply","href":"/scout-soroban/docs/vulnerabilities/divide-before-multiply","docId":"vulnerabilities/divide-before-multiply"},{"type":"link","label":"Unsafe unwrap","href":"/scout-soroban/docs/vulnerabilities/unsafe-unwrap","docId":"vulnerabilities/unsafe-unwrap"},{"type":"link","label":"Unsafe expect","href":"/scout-soroban/docs/vulnerabilities/unsafe-expect","docId":"vulnerabilities/unsafe-expect"},{"type":"link","label":"Overflow check","href":"/scout-soroban/docs/vulnerabilities/overflow-check","docId":"vulnerabilities/overflow-check"},{"type":"link","label":"Insufficiently random values","href":"/scout-soroban/docs/vulnerabilities/insufficiently-random-values","docId":"vulnerabilities/insufficiently-random-values"},{"type":"link","label":"Unprotected update current contract wasm","href":"/scout-soroban/docs/vulnerabilities/unprotected-update-current-contract-wasm","docId":"vulnerabilities/unprotected-update-current-contract-wasm"},{"type":"link","label":"Avoid core::mem::forget usage","href":"/scout-soroban/docs/vulnerabilities/avoid-core-mem-forget","docId":"vulnerabilities/avoid-core-mem-forget"},{"type":"link","label":"Set contract storage","href":"/scout-soroban/docs/vulnerabilities/set-contract-storage","docId":"vulnerabilities/set-contract-storage"},{"type":"link","label":"Avoid panic error","href":"/scout-soroban/docs/vulnerabilities/avoid-panic-error","docId":"vulnerabilities/avoid-panic-error"},{"type":"link","label":"Avoid unsafe block","href":"/scout-soroban/docs/vulnerabilities/avoid-unsafe-block","docId":"vulnerabilities/avoid-unsafe-block"},{"type":"link","label":"DoS unbounded operation","href":"/scout-soroban/docs/vulnerabilities/dos-unbounded-operation","docId":"vulnerabilities/dos-unbounded-operation"},{"type":"link","label":"Soroban version","href":"/scout-soroban/docs/vulnerabilities/soroban-version","docId":"vulnerabilities/soroban-version"},{"type":"link","label":"Unused return enum","href":"/scout-soroban/docs/vulnerabilities/unused-return-enum","docId":"vulnerabilities/unused-return-enum"},{"type":"link","label":"Iterators over indexing","href":"/scout-soroban/docs/vulnerabilities/iterators-over-indexing","docId":"vulnerabilities/iterators-over-indexing"},{"type":"link","label":"Assert violation","href":"/scout-soroban/docs/vulnerabilities/assert-violation","docId":"vulnerabilities/assert-violation"},{"type":"link","label":"Unprotected mapping operation","href":"/scout-soroban/docs/vulnerabilities/unprotected-mapping-operation","docId":"vulnerabilities/unprotected-mapping-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout-soroban/docs/vulnerabilities/dos-unexpected-revert-with-vector","docId":"vulnerabilities/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout-soroban/docs/vulnerabilities/unrestricted-transfer-from","docId":"vulnerabilities/unrestricted-transfer-from"},{"type":"link","label":"Unsafe map get","href":"/scout-soroban/docs/vulnerabilities/unsafe-map-get","docId":"vulnerabilities/unsafe-map-get"},{"type":"link","label":"Incorrect Exponentiation","href":"/scout-soroban/docs/vulnerabilities/incorrect-exponentiation","docId":"vulnerabilities/incorrect-exponentiation"}],"href":"/scout-soroban/docs/vulnerabilities/"},{"type":"category","label":"Detectors","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Divide before multiply","href":"/scout-soroban/docs/detectors/divide-before-multiply","docId":"detectors/divide-before-multiply"},{"type":"link","label":"Unsafe unwrap","href":"/scout-soroban/docs/detectors/unsafe-unwrap","docId":"detectors/unsafe-unwrap"},{"type":"link","label":"Unsafe expect","href":"/scout-soroban/docs/detectors/unsafe-expect","docId":"detectors/unsafe-expect"},{"type":"link","label":"Overflow-check","href":"/scout-soroban/docs/detectors/overflow-check","docId":"detectors/overflow-check"},{"type":"link","label":"Insuficciently random values","href":"/scout-soroban/docs/detectors/insufficiently-random-values","docId":"detectors/insufficiently-random-values"},{"type":"link","label":"Unprotected update of current contract wasm","href":"/scout-soroban/docs/detectors/unprotected-update-current-contract-wasm","docId":"detectors/unprotected-update-current-contract-wasm"},{"type":"link","label":"Avoid core mem forget usage","href":"/scout-soroban/docs/detectors/avoid-core-mem-forget","docId":"detectors/avoid-core-mem-forget"},{"type":"link","label":"Set contract storage","href":"/scout-soroban/docs/detectors/set-contract-storage","docId":"detectors/set-contract-storage"},{"type":"link","label":"Panic error","href":"/scout-soroban/docs/detectors/avoid-panic-error","docId":"detectors/avoid-panic-error"},{"type":"link","label":"Avoid unsafe block","href":"/scout-soroban/docs/detectors/avoid-unsafe-block","docId":"detectors/avoid-unsafe-block"},{"type":"link","label":"DoS unbounded operation","href":"/scout-soroban/docs/detectors/dos-unbounded-operation","docId":"detectors/dos-unbounded-operation"},{"type":"link","label":"Soroban version","href":"/scout-soroban/docs/detectors/soroban-version","docId":"detectors/soroban-version"},{"type":"link","label":"Unused return enum","href":"/scout-soroban/docs/detectors/unused-return-enum","docId":"detectors/unused-return-enum"},{"type":"link","label":"Iterators-over-indexing","href":"/scout-soroban/docs/detectors/iterators-over-indexing","docId":"detectors/iterators-over-indexing"},{"type":"link","label":"Assert violation","href":"/scout-soroban/docs/detectors/assert-violation","docId":"detectors/assert-violation"},{"type":"link","label":"Unprotected Mapping Operation","href":"/scout-soroban/docs/detectors/unprotected-mapping-operation","docId":"detectors/unprotected-mapping-operation"},{"type":"link","label":"DoS unexpected revert with vector","href":"/scout-soroban/docs/detectors/dos-unexpected-revert-with-vector","docId":"detectors/dos-unexpected-revert-with-vector"},{"type":"link","label":"Unrestricted Transfer From","href":"/scout-soroban/docs/detectors/unrestricted-transfer-from","docId":"detectors/unrestricted-transfer-from"},{"type":"link","label":"Unsafe map get","href":"/scout-soroban/docs/detectors/unsafe-map-get","docId":"detectors/unsafe-map-get"},{"type":"link","label":"Zero or test address","href":"/scout-soroban/docs/detectors/zero-or-test-address","docId":"detectors/zero-or-test-address"},{"type":"link","label":"Incorrect Exponentiation","href":"/scout-soroban/docs/detectors/incorrect-exponentiation","docId":"detectors/incorrect-exponentiation"},{"type":"link","label":"Integer overflow or underflow","href":"/scout-soroban/docs/detectors/integer-overflow -or-underflow","docId":"detectors/integer-overflow -or-underflow"}],"href":"/scout-soroban/docs/detectors/"},{"type":"link","label":"Contribute","href":"/scout-soroban/docs/contribute","docId":"contribute"},{"type":"link","label":"Architecture","href":"/scout-soroban/docs/architecture","docId":"architecture"},{"type":"category","label":"Precision and recall","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Scout Bug Fighter for Soroban: Improving Tool\'s Precision","href":"/scout-soroban/docs/precision-and-recall/first-iteration","docId":"precision-and-recall/first-iteration"}],"href":"/scout-soroban/docs/precision-and-recall/"},{"type":"link","label":"Scout GitHub Action","href":"/scout-soroban/docs/github-action","docId":"github-action"},{"type":"link","label":"Scout VS Code Extension","href":"/scout-soroban/docs/vscode-extension","docId":"vscode-extension"},{"type":"link","label":"Scout Soroban Smart Contracts Examples","href":"/scout-soroban/docs/soroban-examples","docId":"soroban-examples"}]},"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\u200b","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-panic-error":{"id":"detectors/avoid-panic-error","title":"Panic error","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/divide-before-multiply":{"id":"detectors/divide-before-multiply","title":"Divide before multiply","description":"Description","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/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":"Description","sidebar":"docsSidebar"},"detectors/iterators-over-indexing":{"id":"detectors/iterators-over-indexing","title":"Iterators-over-indexing","description":"What it does","sidebar":"docsSidebar"},"detectors/overflow-check":{"id":"detectors/overflow-check","title":"Overflow-check","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/set-contract-storage":{"id":"detectors/set-contract-storage","title":"Set contract storage","description":"What it does","sidebar":"docsSidebar"},"detectors/soroban-version":{"id":"detectors/soroban-version","title":"Soroban version","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-update-current-contract-wasm":{"id":"detectors/unprotected-update-current-contract-wasm","title":"Unprotected update of current contract wasm","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-map-get":{"id":"detectors/unsafe-map-get","title":"Unsafe map get","description":"What it does","sidebar":"docsSidebar"},"detectors/unsafe-unwrap":{"id":"detectors/unsafe-unwrap","title":"Unsafe unwrap","description":"Description","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"},"github-action":{"id":"github-action","title":"Scout GitHub Action","description":"At CoinFabrik, we understand the importance of ensuring code quality and security in every step of the development process. That\'s why we\'ve developed a GitHub action to integrate Scout into the CI/CD pipeline.","sidebar":"docsSidebar"},"intro":{"id":"intro","title":"Getting Started","description":"Let\'s discover Scout in less than 5 minutes!.","sidebar":"docsSidebar"},"precision-and-recall/first-iteration":{"id":"precision-and-recall/first-iteration","title":"Scout Bug Fighter for Soroban: Improving Tool\'s Precision","description":"In the scope of the second grant awarded to CoinFabrik by the Stellar Community Fund to advance the development of Scout for Soroban, the focus extends beyond incorporating new detectors and refining features. A key objective of this grant is to subject the tool to rigorous testing against real Soroban projects. Through this process, the aim is to analyze the outcomes meticulously, identifying areas for enhancement to increase the tool\'s precision. This includes minimizing false positives and false negatives, thereby fortifying its efficacy.","sidebar":"docsSidebar"},"precision-and-recall/README":{"id":"precision-and-recall/README","title":"Precision and recall","description":"This section outlines the tasks we perform to enhance the overall quality of Scout.","sidebar":"docsSidebar"},"soroban-examples":{"id":"soroban-examples","title":"Scout Soroban Smart Contracts Examples","description":"In the context of Scout\'s development, we engaged developers without Soroban experience to create a series of smart contracts within tight time constraints, encouraging them to introduce errors.","sidebar":"docsSidebar"},"vscode-extension":{"id":"vscode-extension","title":"Scout VS Code Extension","description":"Add Scout to your development workspace with Scout\'s VS Code extension and run Scout automatically upon saving your file.","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-panic-error":{"id":"vulnerabilities/avoid-panic-error","title":"Avoid panic error","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/avoid-unsafe-block":{"id":"vulnerabilities/avoid-unsafe-block","title":"Avoid unsafe block","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/divide-before-multiply":{"id":"vulnerabilities/divide-before-multiply","title":"Divide before multiply","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/insufficiently-random-values":{"id":"vulnerabilities/insufficiently-random-values","title":"Insufficiently random values","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/iterators-over-indexing":{"id":"vulnerabilities/iterators-over-indexing","title":"Iterators over indexing","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/overflow-check":{"id":"vulnerabilities/overflow-check","title":"Overflow check","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. 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/set-contract-storage":{"id":"vulnerabilities/set-contract-storage","title":"Set contract storage","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/soroban-version":{"id":"vulnerabilities/soroban-version","title":"Soroban version","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-mapping-operation":{"id":"vulnerabilities/unprotected-mapping-operation","title":"Unprotected mapping operation","description":"Description","sidebar":"docsSidebar"},"vulnerabilities/unprotected-update-current-contract-wasm":{"id":"vulnerabilities/unprotected-update-current-contract-wasm","title":"Unprotected update current contract wasm","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-map-get":{"id":"vulnerabilities/unsafe-map-get","title":"Unsafe map get","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"}}}')}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.4b52783b.js b/assets/js/runtime~main.f5172f71.js similarity index 98% rename from assets/js/runtime~main.4b52783b.js rename to assets/js/runtime~main.f5172f71.js index dd00a10c..028187cd 100644 --- a/assets/js/runtime~main.4b52783b.js +++ b/assets/js/runtime~main.f5172f71.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,f,c,b,d={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var f=t[e]={exports:{}};return d[e].call(f.exports,f,f.exports,r),f.exports}r.m=d,e=[],r.O=(a,f,c,b)=>{if(!f){var d=1/0;for(i=0;i=b)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[f,c,b]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var b=Object.create(null);r.r(b);var d={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((a=>d[a]=()=>e[a]));return d.default=()=>e,r.d(b,d),b},r.d=(e,a)=>{for(var f in a)r.o(a,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,f)=>(r.f[f](e,a),a)),[])),r.u=e=>"assets/js/"+({19:"a55a1ff4",53:"935f2afb",143:"db8f9a39",504:"54dff221",672:"9e24d66d",687:"35092f85",845:"561cff20",936:"6f228159",948:"8717b14a",1098:"a51f8b82",1762:"ba326a68",1914:"b01bbd4a",1986:"8f5c116a",2002:"134c7ae3",2158:"4c7b851b",2267:"59362658",2362:"e273c56f",2383:"564d7df9",2384:"d9f32620",2535:"814f3328",2596:"6989e58e",2680:"acdb390b",2814:"656e61e7",2844:"4c297fc1",2891:"ad922fd8",2897:"5ed87147",2942:"2b808466",3085:"1f391b9e",3089:"a6aa9e1f",3237:"1df93b7f",3514:"73664a40",3608:"9e4087bc",3920:"1c76c185",4013:"01a85c17",4066:"e368fbdb",4162:"f27f720e",4205:"124edd55",4266:"320830dc",4307:"31059249",4735:"b8954932",4948:"d427cf8b",4957:"b5078b7a",5019:"c1cdbea7",5038:"5c8c3147",5083:"8c8656b8",5509:"ea2d1f80",5601:"15181e3d",5670:"5c45a5e5",5788:"cea4ec5c",5897:"14aa7e32",5927:"5281b7a2",6103:"ccc49370",6136:"0bd560b9",6167:"29ca9fb7",6233:"e1b53f86",6337:"23c01a81",6395:"ccf06cd0",6468:"038df1ad",6716:"7792a21f",6727:"50561825",7063:"3c4d22c1",7117:"f25a30b0",7414:"393be207",7493:"564989d0",7607:"02f82ad5",7704:"f1edf43b",7725:"ca72c7d5",7918:"17896441",8088:"694c206e",8186:"0b877409",8247:"8c9edb4c",8294:"797dc6d7",8610:"6875c492",8636:"f4f34a3a",8681:"29f775a7",8700:"a1f5d417",8704:"bcc87494",8798:"66964c77",8815:"62bc10b6",8967:"d99c84f2",9003:"925b3f96",9326:"0d5abab5",9514:"1be78505",9549:"dfdcbbec",9598:"83706e8f",9642:"7661071f",9671:"0e384e19",9735:"4ba7e5a3"}[e]||e)+"."+{19:"34596abc",53:"fdab817a",143:"e06134be",504:"3a5b61fd",672:"f80809a5",687:"0f670c7e",845:"fc8b30d9",936:"573a8852",948:"c7a1421f",1098:"01ffa9e6",1762:"5243f40e",1914:"0c684a31",1986:"42879e9d",2002:"1e07b641",2158:"2b90cfaa",2267:"af573dc7",2362:"6c7f8e76",2383:"41629269",2384:"e5299103",2535:"29c7dfa7",2596:"2bd0ea48",2680:"019e8760",2814:"136bdca6",2844:"5d0b8ce5",2891:"dc815d92",2897:"94d09140",2942:"be5e3d5e",3085:"524f0227",3089:"debdf23b",3237:"40a8ede2",3514:"f2b607cf",3608:"1a40bcf2",3920:"1923efbd",4013:"57ffacc3",4066:"1bf317ee",4162:"b2ff8193",4205:"70713835",4266:"cfc2d20c",4307:"e34f94eb",4735:"20470867",4948:"8393f87c",4957:"d0737ec9",5019:"634b6c29",5038:"8ed24dcf",5083:"3823987e",5509:"fc28e661",5601:"0effd055",5670:"75652959",5788:"8340278d",5897:"28e917b3",5927:"69bc2664",6103:"6095ed8c",6136:"6c5c9214",6167:"26fbcbda",6233:"6ff5d3e5",6337:"d5d3b4f8",6395:"17c23e5f",6468:"6df55c25",6716:"c65c53a8",6727:"f362f32c",7063:"f5bb0d58",7117:"b3abacc6",7414:"ad20c4e7",7430:"5ca6a6fe",7493:"3792f37f",7520:"4e259ec9",7607:"0962f15b",7704:"d5445763",7725:"e31c3975",7918:"c3dab571",8088:"1a5d165b",8186:"6441edbf",8247:"a74a3720",8294:"82fccbb5",8610:"06e6f461",8636:"db57f59d",8681:"484e7255",8700:"9ba13acc",8704:"4eaffec0",8798:"5a1dd240",8815:"86ed3fa6",8967:"324c2386",9003:"6c5e07f9",9135:"6fa44598",9326:"dfdbd319",9514:"6ee8e640",9549:"f905354e",9598:"701add88",9642:"391580b9",9671:"951e0490",9735:"545534ac"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},b="scout:",r.l=(e,a,f,d)=>{if(c[e])c[e].push(a);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var b=c[e];if(delete c[e],t.parentNode&&t.parentNode.removeChild(t),b&&b.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/scout-soroban/",r.gca=function(e){return e={17896441:"7918",31059249:"4307",50561825:"6727",59362658:"2267",a55a1ff4:"19","935f2afb":"53",db8f9a39:"143","54dff221":"504","9e24d66d":"672","35092f85":"687","561cff20":"845","6f228159":"936","8717b14a":"948",a51f8b82:"1098",ba326a68:"1762",b01bbd4a:"1914","8f5c116a":"1986","134c7ae3":"2002","4c7b851b":"2158",e273c56f:"2362","564d7df9":"2383",d9f32620:"2384","814f3328":"2535","6989e58e":"2596",acdb390b:"2680","656e61e7":"2814","4c297fc1":"2844",ad922fd8:"2891","5ed87147":"2897","2b808466":"2942","1f391b9e":"3085",a6aa9e1f:"3089","1df93b7f":"3237","73664a40":"3514","9e4087bc":"3608","1c76c185":"3920","01a85c17":"4013",e368fbdb:"4066",f27f720e:"4162","124edd55":"4205","320830dc":"4266",b8954932:"4735",d427cf8b:"4948",b5078b7a:"4957",c1cdbea7:"5019","5c8c3147":"5038","8c8656b8":"5083",ea2d1f80:"5509","15181e3d":"5601","5c45a5e5":"5670",cea4ec5c:"5788","14aa7e32":"5897","5281b7a2":"5927",ccc49370:"6103","0bd560b9":"6136","29ca9fb7":"6167",e1b53f86:"6233","23c01a81":"6337",ccf06cd0:"6395","038df1ad":"6468","7792a21f":"6716","3c4d22c1":"7063",f25a30b0:"7117","393be207":"7414","564989d0":"7493","02f82ad5":"7607",f1edf43b:"7704",ca72c7d5:"7725","694c206e":"8088","0b877409":"8186","8c9edb4c":"8247","797dc6d7":"8294","6875c492":"8610",f4f34a3a:"8636","29f775a7":"8681",a1f5d417:"8700",bcc87494:"8704","66964c77":"8798","62bc10b6":"8815",d99c84f2:"8967","925b3f96":"9003","0d5abab5":"9326","1be78505":"9514",dfdcbbec:"9549","83706e8f":"9598","7661071f":"9642","0e384e19":"9671","4ba7e5a3":"9735"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,f)=>{var c=r.o(e,a)?e[a]:void 0;if(0!==c)if(c)f.push(c[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var b=new Promise(((f,b)=>c=e[a]=[f,b]));f.push(c[2]=b);var d=r.p+r.u(a),t=new Error;r.l(d,(f=>{if(r.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var b=f&&("load"===f.type?"missing":f.type),d=f&&f.target&&f.target.src;t.message="Loading chunk "+a+" failed.\n("+b+": "+d+")",t.name="ChunkLoadError",t.type=b,t.request=d,c[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,f)=>{var c,b,d=f[0],t=f[1],o=f[2],n=0;if(d.some((a=>0!==e[a]))){for(c in t)r.o(t,c)&&(r.m[c]=t[c]);if(o)var i=o(r)}for(a&&a(f);n{"use strict";var e,a,f,c,b,d={},t={};function r(e){var a=t[e];if(void 0!==a)return a.exports;var f=t[e]={exports:{}};return d[e].call(f.exports,f,f.exports,r),f.exports}r.m=d,e=[],r.O=(a,f,c,b)=>{if(!f){var d=1/0;for(i=0;i=b)&&Object.keys(r.O).every((e=>r.O[e](f[o])))?f.splice(o--,1):(t=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[f,c,b]},r.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return r.d(a,{a:a}),a},f=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var b=Object.create(null);r.r(b);var d={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((a=>d[a]=()=>e[a]));return d.default=()=>e,r.d(b,d),b},r.d=(e,a)=>{for(var f in a)r.o(a,f)&&!r.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((a,f)=>(r.f[f](e,a),a)),[])),r.u=e=>"assets/js/"+({19:"a55a1ff4",53:"935f2afb",143:"db8f9a39",504:"54dff221",672:"9e24d66d",687:"35092f85",845:"561cff20",936:"6f228159",948:"8717b14a",1098:"a51f8b82",1762:"ba326a68",1914:"b01bbd4a",1986:"8f5c116a",2002:"134c7ae3",2158:"4c7b851b",2267:"59362658",2362:"e273c56f",2383:"564d7df9",2384:"d9f32620",2535:"814f3328",2596:"6989e58e",2680:"acdb390b",2814:"656e61e7",2844:"4c297fc1",2891:"ad922fd8",2897:"5ed87147",2942:"2b808466",3085:"1f391b9e",3089:"a6aa9e1f",3237:"1df93b7f",3514:"73664a40",3608:"9e4087bc",3920:"1c76c185",4013:"01a85c17",4066:"e368fbdb",4162:"f27f720e",4205:"124edd55",4266:"320830dc",4307:"31059249",4735:"b8954932",4948:"d427cf8b",4957:"b5078b7a",5019:"c1cdbea7",5038:"5c8c3147",5083:"8c8656b8",5509:"ea2d1f80",5601:"15181e3d",5670:"5c45a5e5",5788:"cea4ec5c",5897:"14aa7e32",5927:"5281b7a2",6103:"ccc49370",6136:"0bd560b9",6167:"29ca9fb7",6233:"e1b53f86",6337:"23c01a81",6395:"ccf06cd0",6468:"038df1ad",6716:"7792a21f",6727:"50561825",7063:"3c4d22c1",7117:"f25a30b0",7414:"393be207",7493:"564989d0",7607:"02f82ad5",7704:"f1edf43b",7725:"ca72c7d5",7918:"17896441",8088:"694c206e",8186:"0b877409",8247:"8c9edb4c",8294:"797dc6d7",8610:"6875c492",8636:"f4f34a3a",8681:"29f775a7",8700:"a1f5d417",8704:"bcc87494",8798:"66964c77",8815:"62bc10b6",8967:"d99c84f2",9003:"925b3f96",9326:"0d5abab5",9514:"1be78505",9549:"dfdcbbec",9598:"83706e8f",9642:"7661071f",9671:"0e384e19",9735:"4ba7e5a3"}[e]||e)+"."+{19:"34596abc",53:"0ba531f6",143:"e06134be",504:"3a5b61fd",672:"f80809a5",687:"0f670c7e",845:"fc8b30d9",936:"573a8852",948:"c7a1421f",1098:"01ffa9e6",1762:"5243f40e",1914:"0c684a31",1986:"42879e9d",2002:"1e07b641",2158:"2b90cfaa",2267:"af573dc7",2362:"6c7f8e76",2383:"41629269",2384:"e5299103",2535:"29c7dfa7",2596:"2bd0ea48",2680:"019e8760",2814:"136bdca6",2844:"5d0b8ce5",2891:"dc815d92",2897:"94d09140",2942:"be5e3d5e",3085:"524f0227",3089:"debdf23b",3237:"40a8ede2",3514:"f2b607cf",3608:"1a40bcf2",3920:"1923efbd",4013:"57ffacc3",4066:"1bf317ee",4162:"b2ff8193",4205:"70713835",4266:"cfc2d20c",4307:"e34f94eb",4735:"20470867",4948:"8393f87c",4957:"d0737ec9",5019:"634b6c29",5038:"8ed24dcf",5083:"3823987e",5509:"fc28e661",5601:"0effd055",5670:"75652959",5788:"8340278d",5897:"28e917b3",5927:"69bc2664",6103:"6095ed8c",6136:"6c5c9214",6167:"26fbcbda",6233:"6ff5d3e5",6337:"c1803f90",6395:"17c23e5f",6468:"6df55c25",6716:"c65c53a8",6727:"f362f32c",7063:"f5bb0d58",7117:"b3abacc6",7414:"ad20c4e7",7430:"5ca6a6fe",7493:"3792f37f",7520:"4e259ec9",7607:"0962f15b",7704:"d5445763",7725:"e31c3975",7918:"c3dab571",8088:"1a5d165b",8186:"6441edbf",8247:"a74a3720",8294:"82fccbb5",8610:"06e6f461",8636:"db57f59d",8681:"484e7255",8700:"9ba13acc",8704:"4eaffec0",8798:"5a1dd240",8815:"86ed3fa6",8967:"324c2386",9003:"6c5e07f9",9135:"6fa44598",9326:"dfdbd319",9514:"6ee8e640",9549:"f905354e",9598:"701add88",9642:"391580b9",9671:"951e0490",9735:"545534ac"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},b="scout:",r.l=(e,a,f,d)=>{if(c[e])c[e].push(a);else{var t,o;if(void 0!==f)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var b=c[e];if(delete c[e],t.parentNode&&t.parentNode.removeChild(t),b&&b.forEach((e=>e(f))),a)return a(f)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/scout-soroban/",r.gca=function(e){return e={17896441:"7918",31059249:"4307",50561825:"6727",59362658:"2267",a55a1ff4:"19","935f2afb":"53",db8f9a39:"143","54dff221":"504","9e24d66d":"672","35092f85":"687","561cff20":"845","6f228159":"936","8717b14a":"948",a51f8b82:"1098",ba326a68:"1762",b01bbd4a:"1914","8f5c116a":"1986","134c7ae3":"2002","4c7b851b":"2158",e273c56f:"2362","564d7df9":"2383",d9f32620:"2384","814f3328":"2535","6989e58e":"2596",acdb390b:"2680","656e61e7":"2814","4c297fc1":"2844",ad922fd8:"2891","5ed87147":"2897","2b808466":"2942","1f391b9e":"3085",a6aa9e1f:"3089","1df93b7f":"3237","73664a40":"3514","9e4087bc":"3608","1c76c185":"3920","01a85c17":"4013",e368fbdb:"4066",f27f720e:"4162","124edd55":"4205","320830dc":"4266",b8954932:"4735",d427cf8b:"4948",b5078b7a:"4957",c1cdbea7:"5019","5c8c3147":"5038","8c8656b8":"5083",ea2d1f80:"5509","15181e3d":"5601","5c45a5e5":"5670",cea4ec5c:"5788","14aa7e32":"5897","5281b7a2":"5927",ccc49370:"6103","0bd560b9":"6136","29ca9fb7":"6167",e1b53f86:"6233","23c01a81":"6337",ccf06cd0:"6395","038df1ad":"6468","7792a21f":"6716","3c4d22c1":"7063",f25a30b0:"7117","393be207":"7414","564989d0":"7493","02f82ad5":"7607",f1edf43b:"7704",ca72c7d5:"7725","694c206e":"8088","0b877409":"8186","8c9edb4c":"8247","797dc6d7":"8294","6875c492":"8610",f4f34a3a:"8636","29f775a7":"8681",a1f5d417:"8700",bcc87494:"8704","66964c77":"8798","62bc10b6":"8815",d99c84f2:"8967","925b3f96":"9003","0d5abab5":"9326","1be78505":"9514",dfdcbbec:"9549","83706e8f":"9598","7661071f":"9642","0e384e19":"9671","4ba7e5a3":"9735"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(a,f)=>{var c=r.o(e,a)?e[a]:void 0;if(0!==c)if(c)f.push(c[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var b=new Promise(((f,b)=>c=e[a]=[f,b]));f.push(c[2]=b);var d=r.p+r.u(a),t=new Error;r.l(d,(f=>{if(r.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var b=f&&("load"===f.type?"missing":f.type),d=f&&f.target&&f.target.src;t.message="Loading chunk "+a+" failed.\n("+b+": "+d+")",t.name="ChunkLoadError",t.type=b,t.request=d,c[1](t)}}),"chunk-"+a,a)}},r.O.j=a=>0===e[a];var a=(a,f)=>{var c,b,d=f[0],t=f[1],o=f[2],n=0;if(d.some((a=>0!==e[a]))){for(c in t)r.o(t,c)&&(r.m[c]=t[c]);if(o)var i=o(r)}for(a&&a(f);n Blog | Scout - +

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/archive.html b/blog/archive.html index 3d5692bf..0673bcb1 100644 --- a/blog/archive.html +++ b/blog/archive.html @@ -5,13 +5,13 @@ Archive | Scout - + - + \ No newline at end of file diff --git a/blog/first-blog-post.html b/blog/first-blog-post.html index a9cad623..14ee2e12 100644 --- a/blog/first-blog-post.html +++ b/blog/first-blog-post.html @@ -5,13 +5,13 @@ First Blog Post | Scout - +

First Blog Post

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/long-blog-post.html b/blog/long-blog-post.html index faf3180d..759c0dd0 100644 --- a/blog/long-blog-post.html +++ b/blog/long-blog-post.html @@ -5,13 +5,13 @@ Long Blog Post | Scout - +

Long Blog Post

· 3 min read
Endilie Yacop Sucipto

This is the summary of a very long blog post,

Use a <!-- truncate --> comment to limit blog post size in the list view.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/mdx-blog-post.html b/blog/mdx-blog-post.html index 8cc78831..41a727eb 100644 --- a/blog/mdx-blog-post.html +++ b/blog/mdx-blog-post.html @@ -5,13 +5,13 @@ MDX Blog Post | Scout - +
- + \ No newline at end of file diff --git a/blog/tags.html b/blog/tags.html index 6a3ff47a..caacd410 100644 --- a/blog/tags.html +++ b/blog/tags.html @@ -5,13 +5,13 @@ Tags | Scout - + - + \ No newline at end of file diff --git a/blog/tags/docusaurus.html b/blog/tags/docusaurus.html index 9540caa0..b84deae5 100644 --- a/blog/tags/docusaurus.html +++ b/blog/tags/docusaurus.html @@ -5,13 +5,13 @@ 4 posts tagged with "docusaurus" | Scout - +

4 posts tagged with "docusaurus"

View All Tags

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/tags/facebook.html b/blog/tags/facebook.html index c64f92b1..126a85e4 100644 --- a/blog/tags/facebook.html +++ b/blog/tags/facebook.html @@ -5,13 +5,13 @@ One post tagged with "facebook" | Scout - +

One post tagged with "facebook"

View All Tags

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- + \ No newline at end of file diff --git a/blog/tags/hello.html b/blog/tags/hello.html index 7cfdcb67..2666e2e7 100644 --- a/blog/tags/hello.html +++ b/blog/tags/hello.html @@ -5,13 +5,13 @@ 2 posts tagged with "hello" | Scout - +

2 posts tagged with "hello"

View All Tags

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- + \ No newline at end of file diff --git a/blog/tags/hola.html b/blog/tags/hola.html index 9c1d608d..1701e12c 100644 --- a/blog/tags/hola.html +++ b/blog/tags/hola.html @@ -5,13 +5,13 @@ One post tagged with "hola" | Scout - +

One post tagged with "hola"

View All Tags

· One min read
Gao Wei

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- + \ No newline at end of file diff --git a/blog/welcome.html b/blog/welcome.html index 496345e8..3d9b409a 100644 --- a/blog/welcome.html +++ b/blog/welcome.html @@ -5,13 +5,13 @@ Welcome | Scout - +

Welcome

· One min read
Sébastien Lorber
Yangshun Tay

Docusaurus blogging features are powered by the blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- + \ No newline at end of file diff --git a/docs/architecture.html b/docs/architecture.html index ebd1d748..6e323e7d 100644 --- a/docs/architecture.html +++ b/docs/architecture.html @@ -5,13 +5,13 @@ Architecture | Scout - +

Architecture

Scout Architectural Diagram

Scout is built on Trail of Bits’ 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.

- + \ No newline at end of file diff --git a/docs/contribute.html b/docs/contribute.html index 5638a20a..306198e9 100644 --- a/docs/contribute.html +++ b/docs/contribute.html @@ -5,13 +5,13 @@ Contribute | Scout - +

Contribute

Thank you for your interest in contributing to the development of new detectors.

Getting Started

Create a new issue on our repository with the name of the new detector or test case you wish to contribute. Then, link a new branch to that issue.

If your detector or test case doesn't belong to an existing vulnerability class, please provide documentation for the new vulnerability class you're proposing.

Requirement: All detectors and test cases should follow the kebab-case naming convention, using lowercase and hyphens only.

Detectors

To contribute a new detector:

  1. Choose an appropriate template. Browse our templates at templates/detector. Decide on the early-lint or late-lint template, based on whether you want to lint before or after macro expansion.

  2. Add your modified detector files to a new folder, naming it after your detector, inside the detectors directory.

Test Cases

To contribute a new test case:

  1. Determine the vulnerability class to which your test case belongs. Then, create a new sub-folder under that class in the test-cases directory. Remember to append the detector number at the end, separated by a hyphen.

  2. Within this sub-folder, create two directories: vulnerable-example and remediated-example. Fill each with the relevant files for their respective test cases. If possible, incorporate integration or e2e tests. For guidance, refer to the flipper template in templates/test-case.

- + \ No newline at end of file diff --git a/docs/detectors.html b/docs/detectors.html index 86afe171..da4c227f 100644 --- a/docs/detectors.html +++ b/docs/detectors.html @@ -5,13 +5,13 @@ Detectors | Scout - +

Detectors

In this section we introduce our set of detectors powered by Dylint - a Rust linting tool.

Similar to Clippy, Dylint can run lints to help identify potential issues in code. However, unlike Clippy, Dylint can run lints from user-specified dynamic libraries instead of just a statically predetermined set. This unique feature of Dylint makes it easier for developers to extend and customize their own personal lint collections, leading to reduced compile and run cycles.

Check our Proof of Concept Study for a more detailed analysis of different detection techniques and tools.

- + \ No newline at end of file diff --git a/docs/detectors/assert-violation.html b/docs/detectors/assert-violation.html index b920b829..ba7e2cb6 100644 --- a/docs/detectors/assert-violation.html +++ b/docs/detectors/assert-violation.html @@ -5,13 +5,13 @@ Assert violation | Scout - +
- + \ No newline at end of file diff --git a/docs/detectors/avoid-core-mem-forget.html b/docs/detectors/avoid-core-mem-forget.html index a2c26e75..d821d41b 100644 --- a/docs/detectors/avoid-core-mem-forget.html +++ b/docs/detectors/avoid-core-mem-forget.html @@ -5,13 +5,13 @@ Avoid core mem forget usage | Scout - + - + \ No newline at end of file diff --git a/docs/detectors/avoid-panic-error.html b/docs/detectors/avoid-panic-error.html index 993e498d..fa1014c1 100644 --- a/docs/detectors/avoid-panic-error.html +++ b/docs/detectors/avoid-panic-error.html @@ -5,14 +5,14 @@ Panic error | Scout - +

Panic error

What it does

The panic! macro is used to stop execution when a condition is not met. This is useful for testing and prototyping, but should be avoided in production code.

Why is this bad?

The usage of panic! is not recommended because it will stop the execution of the caller contract.

Known problems

While this linter detects explicit calls to panic!, there are some ways to raise a panic such as unwrap() or expect().

Example

pub fn add(env: Env, value: u32) -> u32 {
let storage = env.storage().instance();
let mut count: u32 = storage.get(&COUNTER).unwrap_or(0);
match count.checked_add(value) {
Some(value) => count = value,
None => panic!("Overflow error"),
}
storage.set(&COUNTER, &count);
storage.extend_ttl(100, 100);
count
}

Use instead:

pub fn add(env: Env, value: u32) -> Result<u32, Error> {
let storage = env.storage().instance();
let mut count: u32 = storage.get(&COUNTER).unwrap_or(0);
match count.checked_add(value) {
Some(value) => count = value,
None => return Err(Error::OverflowError),
}
storage.set(&COUNTER, &count);
storage.extend_ttl(100, 100);
Ok(count)
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/avoid-unsafe-block.html b/docs/detectors/avoid-unsafe-block.html index 3c0fba45..901cf9f0 100644 --- a/docs/detectors/avoid-unsafe-block.html +++ b/docs/detectors/avoid-unsafe-block.html @@ -5,13 +5,13 @@ Avoid unsafe block | Scout - +

Avoid unsafe block

What it does

Checks for usage of unsafe blocks.

Why is this bad?

unsafe blocks should not be used unless absolutely necessary. The use of unsafe blocks in Rust is discouraged because they bypass Rust's memory safety checks, potentially leading to issues like undefined behavior and security vulnerabilities.

Example

pub fn unsafe_function(n: u64) -> u64 {
unsafe {
let mut i = n as f64;
let mut y = i.to_bits();
y = 0x5fe6ec85e7de30da - (y >> 1);
i = f64::from_bits(y);
i *= 1.5 - 0.5 * n as f64 * i * i;
i *= 1.5 - 0.5 * n as f64 * i * i;

let result_ptr: *mut f64 = &mut i;
let result = *result_ptr;

result.to_bits()
}
}

Use instead:

pub fn unsafe_function(n: u64) -> u64 {
let mut i = n as f64;
let mut y = i.to_bits();
y = 0x5fe6ec85e7de30da - (y >> 1);
i = f64::from_bits(y);
i *= 1.5 - 0.5 * n as f64 * i * i;
i *= 1.5 - 0.5 * n as f64 * i * i;
result.to_bits()
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/divide-before-multiply.html b/docs/detectors/divide-before-multiply.html index 700d0091..15bc005e 100644 --- a/docs/detectors/divide-before-multiply.html +++ b/docs/detectors/divide-before-multiply.html @@ -3,15 +3,15 @@ -Divide before multiply | Scout +Divide before multiply | Scout - +
-

Divide before multiply

What it does

Checks the existence of a division before a multiplication.

Why is this bad?

Performing a division operation before multiplication can lead to a loss of precision. It might even result in an unintended zero value.

Example

// Example 1 - Vulnerable
let x = 10;
let y = 6;
let z = x / y * 3; // z evaluates to 3

// Example 2 - Vulnerable
let a = 1;
let b = 2;
let c = a / b * 3; // c evaluates to 0

Use instead:

// Example 1 - Remediated
let x = 10;
let y = 6;
let z = x * 3 / y; // z evaluates to 5

// Example 2 - Remediated
let a = 1;
let b = 2;
let c = a * 3 / b; // c evaluates to 1

Implementation

The detector's implementation can be found at this link.

- +

Divide before multiply

Description

In Rust, the order of operations can influence the precision of the result, especially in integer arithmetic.

Why is this bad?

Performing a division operation before a multiplication can lead to a loss of precision as division between integers might return zero.

Issue example

Consider the following Soroban contract:


pub fn split_profit(percentage: u64, total_profit: u64) -> u64 {
(percentage / 100) * total_profit
}

In this contract, the split_profit function divides the percentage by 100 before multiplying it with total_profit. This could lead to a loss of precision if percentage is less than 100 as the division would return 0. This could lead to incorrect calculations and potential financial loss in a real-world smart contract.

The code example can be found here.

Remediated example

Reverse the order of operations to ensure multiplication occurs before division.


pub fn split_profit(&self, percentage: u64, total_profit: u64) -> u64 {
(percentage * total_profit) / 100
}

The remediated code example can be found here.

How is it detected?

Checks the existence of a division before a multiplication.

References

Rust documentation: Integer Division

+ \ No newline at end of file diff --git a/docs/detectors/dos-unbounded-operation.html b/docs/detectors/dos-unbounded-operation.html index 8b02b773..36137306 100644 --- a/docs/detectors/dos-unbounded-operation.html +++ b/docs/detectors/dos-unbounded-operation.html @@ -5,13 +5,13 @@ DoS unbounded operation | Scout - +

DoS unbounded operation

What it does

This detector checks that when using for or while loops, their conditions limit the execution to a constant number of iterations.

Why is this bad?

If the number of iterations is not limited to a specific range, it could potentially cause out of gas exceptions.

Known problems

False positives are to be expected when using variables that can only be set using controlled flows that limit the values within acceptable ranges.

Example

pub fn unrestricted_loop(for_loop_count: u64) -> u64 {
let mut count = 0;
for i in 0..for_loop_count {
count += i;
}
count
}

Use instead:

const FIXED_COUNT: u64 = 1000;

pub fn restricted_loop_with_const() -> u64 {
let mut sum = 0;
for i in 0..FIXED_COUNT {
sum += i;
}
sum
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/dos-unexpected-revert-with-vector.html b/docs/detectors/dos-unexpected-revert-with-vector.html index 216568aa..316d8d15 100644 --- a/docs/detectors/dos-unexpected-revert-with-vector.html +++ b/docs/detectors/dos-unexpected-revert-with-vector.html @@ -5,13 +5,13 @@ DoS unexpected revert with vector | Scout - +

DoS unexpected revert with vector

What it does

Checks for array pushes without access control.

Why is this bad?

Arrays have a maximum size according to the storage cell. If the array is full, the push will revert. This can be used to prevent the execution of a function.

Known problems

If the owner validation is performed in an auxiliary function, the warning will be shown, resulting in a false positive.

Example

pub fn add_candidate(env: Env, candidate: Address, caller: Address) -> Result<(), URError> {
let mut state = Self::get_state(env.clone());
if Self::vote_ended(env.clone()) {
return Err(URError::VoteEnded);
}
if state.already_voted.contains_key(caller.clone()) {
return Err(URError::AccountAlreadyVoted);
} else {
state.candidates.push_back(candidate.clone());
state.votes.set(candidate, 0);
Ok(())
}
}

Use instead:

pub fn add_candidate(env: Env, candidate: Address, caller: Address) -> Result<(), URError> {
let mut state = Self::get_state(env.clone());
// Require authorization from an admin set at contract initalization.
state.admin.require_auth();
if Self::vote_ended(env.clone()) {
return Err(URError::VoteEnded);
}
if state.already_voted.contains_key(caller.clone()) {
return Err(URError::AccountAlreadyVoted);
} else {
state.candidates.push_back(candidate.clone());
state.votes.set(candidate, 0);
Ok(())
}
}

Or

 pub fn add_candidate(env: Env, candidate: Address, caller: Address) -> Result<(), URError> {
caller.require_auth();
let mut state = Self::get_state(env.clone());
if Self::vote_ended(env.clone()) {
return Err(URError::VoteEnded);
}
if Self::account_has_voted(env.clone(), caller.clone()) {
return Err(URError::AccountAlreadyVoted);
} else {
// Replace the Vector with a mapping like structure made with a DataKey enum.
env.storage().instance().set(&DataKey::Candidate(candidate.clone()), &Candidate{votes: 0});
state.total_candidates += 1;
env.storage().instance().set(&DataKey::State, &state);
Ok(())
}
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/incorrect-exponentiation.html b/docs/detectors/incorrect-exponentiation.html index 39f84a65..728317bb 100644 --- a/docs/detectors/incorrect-exponentiation.html +++ b/docs/detectors/incorrect-exponentiation.html @@ -5,13 +5,13 @@ Incorrect Exponentiation | Scout - +
- + \ No newline at end of file diff --git a/docs/detectors/insufficiently-random-values.html b/docs/detectors/insufficiently-random-values.html index 62e6ccfb..0de63859 100644 --- a/docs/detectors/insufficiently-random-values.html +++ b/docs/detectors/insufficiently-random-values.html @@ -5,13 +5,13 @@ Insuficciently random values | Scout - +

Insuficciently random values

What it does

Checks the usage of ledger().timestamp() or ledger().sequence() for generation of random numbers.

Why is this bad?

Using ledger().timestamp() is not recommended because it could be potentially manipulated by validator. On the other hand, ledger().sequence() is publicly available, an attacker could predict the random number to be generated.

Example

#[contractimpl]
impl Contract {
pub fn generate_random_value_timestamp(env: Env, max_val: u64) -> Result<u64, Error> {
if max_val == 0 {
Err(Error::MaxValZero)
} else {
let val = env.ledger().timestamp() % max_val;
Ok(val)
}
}
pub fn generate_random_value_sequence(env: Env, max_val: u32) -> Result<u32, Error> {
if max_val == 0 {
Err(Error::MaxValZero)
} else {
let val = env.ledger().sequence() % max_val;
Ok(val)
}
}
}

Use instead:

#[contractimpl]
impl Contract {
pub fn generate_random_value(env: Env, max_val: u64) -> Result<u64, Error> {
if max_val == 0 {
Err(Error::MaxValZero)
} else {
let val = env.prng().u64_in_range(0..max_val);
Ok(val)
}
}
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/integer-overflow -or-underflow.html b/docs/detectors/integer-overflow -or-underflow.html index 18bc97fc..68280db6 100644 --- a/docs/detectors/integer-overflow -or-underflow.html +++ b/docs/detectors/integer-overflow -or-underflow.html @@ -5,13 +5,13 @@ Integer overflow or underflow | Scout - +

Integer overflow or underflow

Description

In Rust, arithmetic operations can result in a value that falls outside the allowed numerical range for a given type. When the result exceeds the maximum value of the range, it's called an overflow, and when it falls below the minimum value of the range, it's called an underflow.

Why is this bad?

If there are arithmetic operations with overflow or underflow problems, and if errors are not handled correctly, incorrect results will be generated, bringing potential problems for the contract. Additionally, these types of errors can allow attackers to drain a contract’s funds or manipulate its logic.

Issue example

Consider the following Soroban contract:


pub fn add(env: Env, value: u32) {
let current: u32 = env.storage().temporary().get(&Self::VALUE).unwrap_or(0);
let new_value = current + value;
env.storage().temporary().set(&Self::VALUE, &new_value);
}

In this example, an operation is performed on two u32 values without any safeguards against overflow if it occurs.

The code example can be found here.

Remediated example

pub fn add(env: Env, value: u32) -> Result<(), Error> {
let current: u32 = env.storage().temporary().get(&Self::VALUE).unwrap_or(0);
let new_value = match current.checked_add(value) {
Some(value) => value,
None => return Err(Error::OverflowError),
};
env.storage().temporary().set(&Self::VALUE, &new_value);
Ok(())
}

In this example, the checked_add method is used to perform the addition. It returns the sum if no overflow occurs; otherwise, it returns None, with an OverflowError variant indicating that an overflow error has occurred.

The remediated code example can be found here.

How is it detected?

Checks if there’s any numerical overflow or underflow.

- + \ No newline at end of file diff --git a/docs/detectors/iterators-over-indexing.html b/docs/detectors/iterators-over-indexing.html index 5c537b7a..f99513c6 100644 --- a/docs/detectors/iterators-over-indexing.html +++ b/docs/detectors/iterators-over-indexing.html @@ -5,13 +5,13 @@ Iterators-over-indexing | Scout - +

Iterators-over-indexing

What it does

It warns if the for loop uses indexing instead of iterator. If the indexing goes to length it will not raise a warning.

Why is this bad?

Accessing a vector by index is slower than using an iterator. Also, if the index is out of bounds, it will panic.

Example​

    pub fn sum(e: Env) -> Result<i32, Error> {
let mut ret = 0_i32;
let vec = e
.storage()
.instance()
.get::<DataKey, Vec<i32>>(&DataKey::Data)
.ok_or(Error::NoData)?;
for i in 0..4 {
ret = ret
.checked_add(vec.get(i).ok_or(Error::NoData)?)
.ok_or(Error::IntegerOverflow)?;
}
Ok(ret)
}

Use instead:

    pub fn sum(e: Env) -> Result<i32, Error> {
let mut ret = 0_i32;
let vec = e
.storage()
.instance()
.get::<DataKey, Vec<i32>>(&DataKey::Data)
.ok_or(Error::NoData)?;
for i in vec {
ret = ret.checked_add(i).ok_or(Error::IntegerOverflow)?;
}
Ok(ret)
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/overflow-check.html b/docs/detectors/overflow-check.html index 1b486313..bb6652ca 100644 --- a/docs/detectors/overflow-check.html +++ b/docs/detectors/overflow-check.html @@ -5,7 +5,7 @@ Overflow-check | Scout - + @@ -13,7 +13,7 @@

Overflow-check

What it does

Checks that overflow-checks is enabled in the [profile.release] section of the Cargo.toml.

Why is this bad?

Integer overflow will trigger a panic in debug builds or will wrap in release mode. Division by zero will cause a panic in either mode. In some applications one wants explicitly checked, wrapping or saturating arithmetic.

Example


[package]
name = "overflow-check-vulnerable-1"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
soroban-sdk = "20.0.0-rc2"

[dev-dependencies]
soroban-sdk = { version = "=20.0.0", features = ["testutils"] }

[features]
testutils = ["soroban-sdk/testutils"]

[profile.release]
opt-level = "z"
overflow-checks = false
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true

[profile.release-with-logs]
inherits = "release"
debug-assertions = true

Use instead:


[package]
name = "overflow-check-remediated-1"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
soroban-sdk = "20.0.0-rc2"

[dev-dependencies]
soroban-sdk = { version = "=20.0.0", features = ["testutils"] }

[features]
testutils = ["soroban-sdk/testutils"]

[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true

[profile.release-with-logs]
overflow-checks = true
inherits = "release"
debug-assertions = true

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/set-contract-storage.html b/docs/detectors/set-contract-storage.html index 0939ba0d..2ade60cb 100644 --- a/docs/detectors/set-contract-storage.html +++ b/docs/detectors/set-contract-storage.html @@ -5,13 +5,13 @@ Set contract storage | Scout - +

Set contract storage

What it does

Checks for calls to env.storage() without a prior call to env.require_auth().

Why is this bad?

Functions using keys as variables without proper access control or input sanitation can allow users to perform changes in arbitrary memory locations.

Known problems

Only check the function call, so false positives could result.

Example

fn set_contract_storage(env: Env) {
let _storage = env.storage().instance();
}

Use instead:

fn set_contract_storage(env: Env, user: Address) {
user.require_auth();
let _storage = env.storage().instance();
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/soroban-version.html b/docs/detectors/soroban-version.html index 93576209..e23719f4 100644 --- a/docs/detectors/soroban-version.html +++ b/docs/detectors/soroban-version.html @@ -5,13 +5,13 @@ Soroban version | Scout - + - + \ No newline at end of file diff --git a/docs/detectors/unprotected-mapping-operation.html b/docs/detectors/unprotected-mapping-operation.html index 29a6416f..c92f8299 100644 --- a/docs/detectors/unprotected-mapping-operation.html +++ b/docs/detectors/unprotected-mapping-operation.html @@ -5,13 +5,13 @@ Unprotected Mapping Operation | Scout - +

Unprotected Mapping Operation

What it does

It warns you if a mapping operation (insert, take, remove) function is called with a user-given key field of the type AccountId.

Why is this bad?

Modifying mappings with an arbitrary key given by users can be a significant vulnerability for several reasons:

  • 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.

  • 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.

  • 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.

Known problems

Example

    pub fn set_balance(env: Env, address: Address, balance: i128) -> State {
// Get the current state.
let mut state = Self::get_state(env.clone());

// Set the new account to have total supply if it doesn't exist.
if !state.balances.contains_key(address.clone()) {
state.balances.set(address, balance);
// Save the state.
env.storage().persistent().set(&STATE, &state);
}

state
}

Use instead:

    pub fn set_balance(env: Env, address: Address, balance: i128) -> State {
// Authenticate user
address.require_auth();

// Get the current state.
let mut state = Self::get_state(env.clone());

// Set the new account to have total supply if it doesn't exist.
if !state.balances.contains_key(address.clone()) {
state.balances.set(address, balance);
// Save the state.
env.storage().persistent().set(&STATE, &state);
}

state
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/unprotected-update-current-contract-wasm.html b/docs/detectors/unprotected-update-current-contract-wasm.html index 2cef2a6f..3af90613 100644 --- a/docs/detectors/unprotected-update-current-contract-wasm.html +++ b/docs/detectors/unprotected-update-current-contract-wasm.html @@ -5,13 +5,13 @@ Unprotected update of current contract wasm | Scout - +

Unprotected update of current contract wasm

What it does

It warns you if update_current_contract_wasm() function is called without a previous check of the address of the caller.

Why is this bad?

If users are allowed to call update_current_contract_wasm(), 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.

Example

#[contractimpl]
impl UpgradeableContract {
pub fn init(e: Env, admin: Address) {
e.storage().instance().set(&DataKey::Admin, &admin);
}

pub fn version() -> u32 {
1
}

pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) {
let admin: Address = e.storage().instance().get(&DataKey::Admin).unwrap();

e.deployer().update_current_contract_wasm(new_wasm_hash);
}
}

Use instead:

#[contractimpl]
impl UpgradeableContract {
pub fn init(e: Env, admin: Address) {
e.storage().instance().set(&DataKey::Admin, &admin);
}

pub fn version() -> u32 {
1
}

pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) {
let admin: Address = e.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();

e.deployer().update_current_contract_wasm(new_wasm_hash);
}
}

Implementation

The detector's implementation can be found at this link

- + \ No newline at end of file diff --git a/docs/detectors/unrestricted-transfer-from.html b/docs/detectors/unrestricted-transfer-from.html index 13f07041..75277694 100644 --- a/docs/detectors/unrestricted-transfer-from.html +++ b/docs/detectors/unrestricted-transfer-from.html @@ -5,13 +5,13 @@ Unrestricted Transfer From | Scout - +

Unrestricted Transfer From

What it does

It warns you if a transfer_from function is called with a user-defined parameter in the from field.

Why is this bad?

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.

Example

pub fn deposit(env: Env, from: Address) -> Result<(), UTFError> {
let mut state: State = Self::get_state(env.clone())?;
state.buyer.require_auth();
if state.status != Status::Created {
return Err(UTFError::StatusMustBeCreated);
}
let token_client = token::Client::new(&env, &state.token);
token_client.transfer_from(
&env.current_contract_address(),
&from,
&env.current_contract_address(),
&state.amount,
);
state.status = Status::Locked;
env.storage().instance().set(&STATE, &state);
Ok(())
}

Use instead:

  pub fn deposit(env: Env) -> Result<(), UTFError> {
let mut state: State = Self::get_state(env.clone())?;
state.buyer.require_auth();
if state.status != Status::Created {
return Err(UTFError::StatusMustBeCreated);
}
let token_client = token::Client::new(&env, &state.token);
token_client.transfer_from(
&env.current_contract_address(),
&state.buyer,
&env.current_contract_address(),
&state.amount,
);
state.status = Status::Locked;
env.storage().instance().set(&STATE, &state);
Ok(())
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/unsafe-expect.html b/docs/detectors/unsafe-expect.html index 95acca8b..f3f4429d 100644 --- a/docs/detectors/unsafe-expect.html +++ b/docs/detectors/unsafe-expect.html @@ -5,13 +5,13 @@ Unsafe expect | Scout - +

Unsafe expect

What it does

Checks for usage of .expect()

Why is this bad?

.expect() might panic if the result value is an error or None.

Example

// example code where a warning is issued
fn main() {
let result = result_fn().expect("error");
}
fn result_fn() -> Result<u8, Error> {
Err(Error::new(ErrorKind::Other, "error"))
}

Use instead:

// example code that does not raise a warning
fn main() {
let result = if let Ok(result) = result_fn() {
result
}
}
fn result_fn() -> Result<u8, Error> {
Err(Error::new(ErrorKind::Other, "error"))
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/unsafe-map-get.html b/docs/detectors/unsafe-map-get.html index a8cd8418..5476af73 100644 --- a/docs/detectors/unsafe-map-get.html +++ b/docs/detectors/unsafe-map-get.html @@ -5,13 +5,13 @@ Unsafe map get | Scout - +

Unsafe map get

What it does

This detector identifies instances where unsafe methods like get, get_unchecked, and try_get_unchecked are used on Map objects in Soroban smart contracts.

Why is this bad?

These methods are risky because they can lead to panics if the key does not exist in the map. Using these methods without proper checks increases the risk of runtime errors that can disrupt the execution of the smart contract and potentially lead to unexpected behavior or denial of service.

Example

pub fn get_from_map(env: Env) -> Option<i32> {
let map: Map<Val, Val> = map![&env, (1i32.into_val(&env), 2i64.into_val(&env))];
let map: Val = map.into();
let map: Map<i32, i32> = map.try_into_val(&env).unwrap();
map.get(1)
}

Use instead:

pub fn get_map_with_different_values(env: Env, key: i32) -> Result<Option<i32>, Error> {
let map: Map<Val, Val> = map![
&env,
(1i32.into_val(&env), 2i32.into_val(&env)),
(3i32.into_val(&env), 4i64.into_val(&env)),
];
let map: Val = map.into();
let map: Map<i32, i32> = map.try_into_val(&env).unwrap();
map.try_get(key).map_err(Error::from)
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/unsafe-unwrap.html b/docs/detectors/unsafe-unwrap.html index 8902d022..3f927695 100644 --- a/docs/detectors/unsafe-unwrap.html +++ b/docs/detectors/unsafe-unwrap.html @@ -5,13 +5,13 @@ Unsafe unwrap | Scout - +

Unsafe unwrap

Description

In Rust, the unwrap method is commonly used for error handling. It retrieves the inner value of an Option or Result. If an error or None occurs, it calls panic! without a custom error message.

Why is this bad?

.unwrap() might panic if the result value is an error or None. It is recommended to avoid the panic of a contract because it stops its execution, which might lead the contract to an inconsistent state if the panic occurs in the middle of state changes. Additionally, the panic could cause a transaction to fail.

Issue example

Consider the following Soroban contract:

#[contractimpl]
impl UnsafeUnwrap {
pub fn unwrap(n: u64) -> u64 {
let result = Self::non_zero_or_error(n);
result.unwrap()
}

pub fn non_zero_or_error(n: u64) -> Result<u64, Error> {
if n == 0 {
return Err(Error::CustomError);
}
Ok(n)
}
}

In this contract, the unwrap function uses the unwrap method to save the result of the non_zero_or_error function. If the function returns Err, the contract will panic and halt execution, potentially leading to malicious exploitation to disrupt the contract's operation.

The code example can be found here.

Remediated example

Instead of using unwrap, use a safer method for error handling. In this case, if the function returns Err, it will return a default value (like 0).

#[contractimpl]
impl UnsafeUnwrap {
pub fn unwrap_or_default(n: u64) -> u64 {
let result = Self::non_zero_or_error(n);
result.unwrap_or(0)
}

pub fn non_zero_or_error(n: u64) -> Result<u64, Error> {
if n == 0 {
return Err(Error::CustomError);
}
Ok(n)
}
}

The remediated code example can be found here.

How is it detected?

Checks for usage of .unwrap()

References

Rust documentation: unwrap

- + \ No newline at end of file diff --git a/docs/detectors/unused-return-enum.html b/docs/detectors/unused-return-enum.html index a778c381..81e67e41 100644 --- a/docs/detectors/unused-return-enum.html +++ b/docs/detectors/unused-return-enum.html @@ -5,13 +5,13 @@ Unused return enum | Scout - +

Unused return enum

What it does

It warns if a function that returns a Result type does not return the Result enum variant (Ok/Err).

Why is this bad?

If any of the variants (Ok/Err) is not used, the code could be simplified or it could imply a bug.

Known problems

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.

Example

Instead of using:

#![no_std]

use soroban_sdk::{contract, contracterror, contractimpl};

#[contract]
pub struct UnusedReturnEnum;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
/// An overflow was produced.
Overflow = 1,
}

#[contractimpl]
impl UnusedReturnEnum {
/// Returns the percentage difference between two values.
pub fn get_percentage_difference(balance1: u128, balance2: u128) -> Result<u128, Error> {
let absolute_difference = balance1.abs_diff(balance2);
let sum = balance1 + balance2;

match 100u128.checked_mul(absolute_difference / sum) {
Some(result) => result,
None => panic!("Overflow"),
};

Err(Error::Overflow)
}
}

Use this:

#![no_std]

use soroban_sdk::{contract, contracterror, contractimpl, testutils::arbitrary::arbitrary::Result};

#[contract]
pub struct UnusedReturnEnum;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
/// An overflow was produced.
Overflow = 1,
}

#[contractimpl]
impl UnusedReturnEnum {
/// Returns the percentage difference between two values.
pub fn get_percentage_difference(balance1: u128, balance2: u128) -> Result<u128, Error> {
let absolute_difference = balance1.abs_diff(balance2);
let sum = balance1 + balance2;

match 100u128.checked_mul(absolute_difference / sum) {
Some(result) => Ok(result),
None => Err(Error::Overflow),
}
}
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/detectors/zero-or-test-address.html b/docs/detectors/zero-or-test-address.html index 35862039..23bceeab 100644 --- a/docs/detectors/zero-or-test-address.html +++ b/docs/detectors/zero-or-test-address.html @@ -5,13 +5,13 @@ Zero or test address | Scout - +

Zero or test address

What it does

Checks whether the zero address is being inputed to a function without validation.

Why is this bad?

Because the private key for the zero address is known, anyone could take ownership of the contract.

Example

pub fn set(e: Env, admin: Address, data: i32) -> Result<(), Error> {
if !ZeroAddressContract::ensure_is_admin(&e, admin)? {
return Err(Error::NotAdmin);
}
e.storage().persistent().set(&DataKey::Data, &data);
Ok(())
}

Use instead:

pub fn set(e: Env, admin: Address, data: i32) -> Result<(), Error> {
if admin
== Address::from_string(&String::from_bytes(
&e,
b"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
))
{
return Err(Error::InvalidNewAdmin);
}
if !ZeroAddressContract::ensure_is_admin(&e, admin)? {
return Err(Error::NotAdmin);
}
e.storage().persistent().set(&DataKey::Data, &data);
Ok(())
}

Implementation

The detector's implementation can be found at this link.

- + \ No newline at end of file diff --git a/docs/github-action.html b/docs/github-action.html index f74cd69b..ef1bf97e 100644 --- a/docs/github-action.html +++ b/docs/github-action.html @@ -5,13 +5,13 @@ Scout GitHub Action | Scout - +

Scout GitHub Action

At CoinFabrik, we understand the importance of ensuring code quality and security in every step of the development process. That's why we've developed a GitHub action to integrate Scout into the CI/CD pipeline.

Scout is triggered upon every commit pushed to a pull request, automatically running the tool against the targeted smart contracts. This immediate feedback loop allows developers to quickly address any issues before merging the code into the main branch, reducing the risk of introducing bugs or vulnerabilities.

Quick Start

To integrate Scout into your CI/CD pipeline, simply add the following scout.yml to the .github/workflows directory in your repo.

name: scout-workflow
on: [push]

jobs:
scout-audit:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
repository-projects: write
steps:
- name: checkout
uses: actions/checkout@v2

- name: do scout
uses: coinfabrik/scout-actions@v2.4
with:
target: # Path to the root of your smart contract (e.g. contracts/token/)
markdown_output: "true"

- uses: mshick/add-pr-comment@v2.8.2
with:
message-path: ${{ github.workspace }}/report.md

Considerations

  1. Make sure that your smart contract compiles correctly. Scout will not run if any compilation errors exist.
  2. Check that target in scout.yml is set to the root of the smart contract (where the Cargo.toml file is).
  3. To properly see Scout's results, make sure that you have an open pull request to which you are committing your changes, as Scout's results will be shown as a comment in the PR.

Output Example

Scout results are display as a comment in the pull request.

Scout Action output example.

- + \ No newline at end of file diff --git a/docs/intro.html b/docs/intro.html index 13cc89ff..b2b5a7a2 100644 --- a/docs/intro.html +++ b/docs/intro.html @@ -5,14 +5,14 @@ Getting Started | Scout - +

Getting Started

Let's discover Scout in less than 5 minutes!.

About Scout

Scout is an extensible open-source tool intended to assist Stellar's Soroban smart contract developers and auditors detect common security issues and deviations from best practices. This tool helps developers write secure and more robust smart contracts.

Features

  • A list of vulnerabilities, best practices and enhancements, together with associated detectors to identify these issues in your code
  • Command Line Interface (CLI)
  • VS Code Extension

What you'll need

Make sure that Cargo is installed on your computer. For using the VSCode Extension you must be using VS Code.

You should be able to install and run Scout without issues on Mac or Linux. You can also use it in Windows through WSL.

Command Line Interface (CLI)

The command line interface is designed to allow you to run Scout on an entire project. It is especially useful for auditing or performing a final review of your code.

Installation

Make sure that Cargo is installed on your computer. Then, install Scout with the following command:

cargo install cargo-scout-audit

Usage

To run Scout on your project execute the following command:

cargo scout-audit

💡 Scout supports Cargo Workspaces. When run on a workspace, Scout will be executed on all packages specified as members of the workspace.

⚠️ Make sure that your smart contracts compile properly. Scout won't run if any compilation errors exist.

In the table below, we specify all the option available for the CLI.

Command/OptionExplanation
cargo scout-auditRuns the static analyzer on the current directory
cargo scout-audit --helpProvides a brief explanation of all the available commands and their usage.
cargo scout-audit --manifest-path <PATH_TO_CARGO_TOML>This option is used to specify the path to the Cargo.toml file that you want to analyze.
cargo scout-audit --profile <PROFILE_NAME>This option allows you to analyze code using specific group of detectors, configured previously on $HOME/.config/scout/(ink/soroban)-config.toml
cargo scout-audit --filter <DETECTOR_LIST_SEPARATED_BY_COMAS>This option allows you to analyze code using specific detectors. Provide a comma-separated list of detectors for this purpose.
cargo scout-audit --exclude <DETECTOR_LIST_SEPARATED_BY_COMAS>With this command, you can exclude specific detectors from the analysis. You need to give a comma-separated list of the detectors to be excluded.
cargo scout-audit --list-detectorsDisplay a list of all available detectors.
cargo scout-audit --versionDisplays the current version of the static analyzer.
cargo scout-audit --verbosePrint additional information on run
cargo scout-audit --local-detectors <PATH_TO_FOLDER>Uses the detectors of a local folder. This considers the sub-folders as detectors.
cargo scout-audit --output-format [text,json,html,sarif,pdf,md,markdown]Sets the output format. Selecting json, html, sarif, markdown, or pdf will create a file with the output
cargo scout-audit --output-path <PATH_TO_OUTPUT_FILE>Sets the output path. If a format was selected, this will replace the default file with the given one

Profile configuration

The profile configuration file is generated automatically in $HOME/.config/scout/soroban-config.toml the first time scout-audit is run. The configuration has the following format

[<profile-name>.<detector-name>]
enabled = <true|false>

For example, if you want to define a profile named 'dev' in which the 'panic-error' detector is disabled and the 'soroban-version' detector is enabled, you should do the following:

[dev.panic-error]
enabled = false
[dev.soroban-version]
enabled = true

HTML Vulnerability Report

We've upgraded Scout's HTML output to introduce a comprehensive HTML Vulnerability Report, enhancing your ability to quickly assess and address the security status of your project. The new features included in the report are designed to provide a detailed yet concise overview of the findings.

html

Usage: cargo scout-audit --output-format html

VSCode Extension

We built the Scout VSCode Extension to help developers write secure and more robust smart contracts. Listing security issues, and highlighting issues with squiggles and hover-over descriptions, we hope our extension will help you catch vulnerabilities during development.

Installation

Install Scout from the Marketplace within the Extensions tab of Visual Studio Code. You can find the extension here.

You'll also need to have installed the CLI, as the extension uses the CLI to perform the analysis. You can find instructions for installing the CLI here.

Usage

After you've installed the extension, simply open a project workspace that contains any Soroban (.rs) files. You will see potential issues and warnings via a wiggle underline of the relevant code.

Troubleshooting Guide

1. Installation Troubleshooting

Issue: Difficulties installing dependencies and Scout.

Solution:

  • For Dylint:

To install necessary libraries for Dylint, run the following commands:

sudo apt install libssl-dev
sudo apt install pkg-config
  • For C Compiler (gcc).

To install gcc, which is required for some components, use:

sudo apt install gcc
  • For error error[E0658].

When encountering this error error[E0658]: use of unstable library feature 'stdsimd', run cargo clean and ensure you are using this version of rustup:

 cargo clean
rustup default nightly-2023-12-16

2. Crossed Contract Calls

Issue: Scout encounters issues when analyzing contracts that perform crossed calls.

Solution:

  • When encountering problems with crossed calls, it's beneficial to compile the dependent contract first. Run the following command to build the second contract:
  soroban contract build
- + \ No newline at end of file diff --git a/docs/precision-and-recall.html b/docs/precision-and-recall.html index cb8c0ed7..36071ad0 100644 --- a/docs/precision-and-recall.html +++ b/docs/precision-and-recall.html @@ -5,13 +5,13 @@ Precision and recall | Scout - + - + \ No newline at end of file diff --git a/docs/precision-and-recall/first-iteration.html b/docs/precision-and-recall/first-iteration.html index c227cdd0..502df4a3 100644 --- a/docs/precision-and-recall/first-iteration.html +++ b/docs/precision-and-recall/first-iteration.html @@ -5,13 +5,13 @@ Scout Bug Fighter for Soroban: Improving Tool's Precision | Scout - +

Scout Bug Fighter for Soroban: Improving Tool's Precision

In the scope of the second grant awarded to CoinFabrik by the Stellar Community Fund to advance the development of Scout for Soroban, the focus extends beyond incorporating new detectors and refining features. A key objective of this grant is to subject the tool to rigorous testing against real Soroban projects. Through this process, the aim is to analyze the outcomes meticulously, identifying areas for enhancement to increase the tool's precision. This includes minimizing false positives and false negatives, thereby fortifying its efficacy.

In this document we describe the work and achievements made during this first iteration.

Scout for Soroban: Current Status

At the end of January, we launched the first prototype of Scout for Soroban. Over the last two months, our focus has been on maturing the tool, taking it one step forward to make it a useful tool for every Soroban developer.

Currently, the tool offers the following features:

  • A CLI tool.
  • Detection capabilities for 19 warnings (and growing).
    • 4 enhancement suggestions.
    • 2 minor vulnerabilities.
    • 7 medium vulnerabilities.
    • 6 critical vulnerabilities.
  • Different output options so that users can chose the one that best suit their needs (HTML, markdown, pdf and JSON).
  • A VS Code extension to integrate Scout into the development workspace.
  • A GitHub action to include Scout in the CI/CD workflow.

Validating Scout on Real Life Projects

In order to understand how Scout fares in a real-world scenario, we ran the tool on 71 smart contracts of 18 public Soroban projects and measured its precision and recall. Precision is directly related to false positives, as the ratio of true positives over the false and true positives (or the rate of correctly triggered alarms). Recall relates to false negatives. It is the ratio of true positives over the sum of true positives and false negatives (or the rate of issues found among those available).

After running Scout (cargo-scout-audit version 0.2.4) on the smart contracts, we identified a total of 847 triggered alarms, out of which 290 were determined to be false positives following a manual review of each finding. This results in a false positive rate of 34.24%. We further analyzed the false positives associated with each detector, focusing particularly on unsafe-unwrap and set-contract-storage, the two detectors with the highest number of false positives, to identify potential improvements to the tool's precision.

We subsequently refined the detectors and released an updated version of Scout (cargo-scout-audit version 0.2.6), which included enhancements. We then re-ran the tool, focusing on the revised detectors. Our modifications were not limited to the two detectors that produced false positives; we also adjusted other detectors that we believed could potentially lead to false positives in similar situations. As a result, our analysis led to improvements on five detectors.

In addition to analyzing Scout as a single source of triggers, we conducted two other analyses (refer to Appendices section below). Firstly, we examined the rates of false positives per smart contract/project, which reflects the perceived quality from the user's perspective (those who would run the tool in their project individually). Secondly, we assessed the rate of false positives per detector to determine the performance of each detector and identify areas needing improvement.

We have already begun the next iteration of Precision and Recall, focusing on further refining Scout's detectors. We will conduct new runs of the tool and analyze the results, including the latest detectors additions. This analysis will enable us to confirm the final rate of false positives after the improvements, completing Table 2: False Positives per Detector.

Improvements on Detectors

As we analyzed the results from running the tool, we identified that most of the false positives occur in unsafe-unwrap and set-contract-storage detectors. We focused our work on improving the precision of these two detectors, as well as other detectors that could be enhanced from similar checks.

On unsafe-unwrap

For unsafe-unwrap, we noticed cases where previous checks in the analyzed code made the particular use of unwrap() not result in an error. We updated the detector to validate whether these checks are present in the code, decreasing the amount of false positive detections on a second run of the tool.

Example 1: False positive for unsafe unwrap with previous check.

pub  fn  truncating_mul(self:  &Self,  x:  i128)  ->  i128 {
let result = safe_mul(x, self.num, self.den);
if result.is_err(){
panic!("integer overflow")
}
result.unwrap()
}

Previously, our unsafe-unwrap detector would generate a warning for the usage of unwrap in this context, even though the function would never reach that part of the code without a confirmed existing value. Now, our detector can handle various scenarios for both Result and Option types. It can appropriately manage cases where the user might halt execution of a function upon encountering an error (as shown above) or use unwrap safely within an is_some() or is_ok() block. Furthermore, the detector is capable of addressing conditions involving or operators with types that are either the same or different, treating each case individually (e.g. using is_some() on one variable, and is_err() on another one).

We also registered another class of false positives, which, due to the particular arithmetic and value assignment of the variables involved, would probably not result in a vulnerability, but found no way to discard them for this detector within the restrictions of our static analysis method.

Example 2: False positive due to arithmetic and value assignment in range. If the values assigned to variables do not exceed the range, unwrap() will not return an error.

pub  fn  some_function(e:  &Env)  {
let mut total = get_total(e);
total = total.checked_add(1).unwrap();
total
}

Finally, we identified that the same checks could be applied to the detector unsafe-expect, and updated it accordingly.

On set-contract-storage

Upon analyzing false positives in the set-contract-storage detector, we identified use cases where the authorization to use env.storage() was done in a function outside of the analysis context of our detector, or the storage method being detected (e.g: get) did not represent a vulnerability.

We extended the analysis context of our detector to identify these authorizations in parent functions and added the capability for the detector to now differentiate between various storage types from the Soroban SDK.

Example 3: False positive for set contract storage. This example authorizes the call of storage and uses get which is non vulnerable.

pub  fn  some_function(  env:  Env)  ->  u32  {
let storage = env.storage().persistent();
if storage
.get::<_, SomeState>(&DataKey::SomeState)
.is_none() {
panic_with_error!(&env, Error::NotInitialized);
}

let admin = storage.get::<_, Address (&DataKey::Admin).unwrap();
admin.require_auth();
...
}

On the other hand, we believe that some use cases using DataKey could now result in true positives, which are being discarded after the detector’s update to differentiate between storage types. When the key used to modify the storage is not of type soroban_sdk:Address, but an enum DataKey, the detector overlooks the issue, without validating if a user address is being modified and if it represents a vulnerability. We are currently evaluating these cases to amend our detector.

Example 4: New False negative for set contract storage. This example is no longer detected after our update because the DataKey is not of type soroban_sdk:Address.

env.storage().instance().set(&DataKey::State,  &State::new(storage,  adder,  subber));

The same extension of the analysis context was also applied on detectors unprotected-mapping-operation and unprotected-update-contract-wasm.

Enhanced Authentication Detection: Context-Aware Analysis

We have introduced a new feature that significantly enhances the capability of many of our detectors by making them inter-procedural context-aware. Previously, many authentication patterns caused our detectors to issue false warnings: alerts that were triggered even when the necessary verifications had been correctly executed. Our refined approach involves creating a map that includes methods and the methods they invoke. This allows us to defer analysis until all relevant methods have been reviewed. By doing this, we can maintain a graph of functions, aimed at minimizing false positives. This enhancement is particularly beneficial for authentication-related detectors, as it enables the construction of a tree of authenticated methods, ensuring more accurate detection and fewer errors.

Improvements on Troubleshooting Documentation

As we used Scout over a variety of projects, we noticed some issues when running the tool on contracts performing crossed calls. For these cases we found that a solution is compiling the second contract first (soroban contract build) before running Scout on the first one.

On the other hand, as we tried Scout on different environments, we noticed some installation caveats. We wrote down a troubleshooting guide to aid the user on particular installation issues.

Appendices

Appendix 1: False Positive Alarms per Project

Analyzing the number of false positives per project, we observe an average rate of false positives vs total positives of 21 %, and a median of 0%.

If we analyze only projects with detections, the average rate of false positives vs total positives per project increases to 48%, and the median to 51%.

We keep the identity of the analyzed projects anonymous as we confirm and responsibly disclose true positives found during our analysis of the tool’s output.

The detectors run correspond to the ones available in the Scout version 0.2.4 at the commencement of this analysis.

Table 1: False positives per project

Project IDTotal PositivesFalse Positives% False Positives
127415%
22000%
3000%
4000%
55000%
65500%
71000%
84800%
9723549%
102500%
111221714%
12441739%
131000%
14706390%
151200%
16151067%
17472860%
1822011653%

Appendix 2: False Positive Alarms per Detector

In the following table, we identify the total number of positives and false positives per detector across all analyzed smart contracts. The detectors run correspond to the ones available in the Scout version 0.2.4 at the beginning of this analysis. Notice that some detectors were never activated in the analyzed code. The false positives were analyzed in order to improve the detectors.

Table 2: False positives per detector

DetectorTotal PositivesFalse Positives% False Positives
Divide before multiply000.00%
Unsafe unwrap18063.33%
Unsafe expect6500.00%
Overflow check200.00%
Insufficiently random values000.00%
Unprotected update current contract wasm000.00%
Avoid core mem forget000.00%
Set contract storage47828459.41%
Avoid panic error6300.00%
Avoid unsafe block000.00%
Dos unbounded operation1300.00%
Soroban version4600.00%
Total84729034.24%

Some of the detectors named above are highlighted, meaning we focused our analysis on them and worked primarily to improve those.

- + \ No newline at end of file diff --git a/docs/soroban-examples.html b/docs/soroban-examples.html index ece09b46..99519978 100644 --- a/docs/soroban-examples.html +++ b/docs/soroban-examples.html @@ -5,13 +5,13 @@ Scout Soroban Smart Contracts Examples | Scout - +

Scout Soroban Smart Contracts Examples

In the context of Scout's development, we engaged developers without Soroban experience to create a series of smart contracts within tight time constraints, encouraging them to introduce errors.

Following this, a senior auditor from CoinFabrik conducted a security review of these smart contracts, focusing on vulnerabilities, deviations from best practices, and potential improvements.

The objective was to obtain a set of smart contracts for Scout testing purposes, allowing us to compare the tool's results with the auditor's findings and refine Scout accordingly, such as by adding new detectors or enhancing existing ones.

Moreover, these smart contracts will serve as educational resources for the Soroban developer community. The security review findings will also help raise awareness about common issues.

List of Implemented Smart Contracts

Follow the links to the smart contract. Each of them includes a README file with an overview, the functions implemented, and a description of each, as well as instructions on how to interact with the contract.

Security Review

The security review reported 20 issues and 9 enhancements.

👉 Navigate to this link to view the security review.

⚠️ Take into consideration that this is not a full security audit. Use these smart contracts with caution.

- + \ No newline at end of file diff --git a/docs/vscode-extension.html b/docs/vscode-extension.html index d60252b3..409c581d 100644 --- a/docs/vscode-extension.html +++ b/docs/vscode-extension.html @@ -5,13 +5,13 @@ Scout VS Code Extension | Scout - +

Scout VS Code Extension

Add Scout to your development workspace with Scout's VS Code extension and run Scout automatically upon saving your file.

Scout VS Code extension.

⚠️ To ensure the extension runs properly, make sure that you open the directory containing your smart contract, rather than the entire project. For example, if your smart contracts are located in myproject/contracts, and you want to work on the token contract while using the Scout VS Code Extension, open myproject/contracts/token.

💡 Tip: To see the errors highlighted in your code, we recommend installing the Error Lens Extension.

👉 Download Scout VS Code from Visual Studio Marketplace.

- + \ No newline at end of file diff --git a/docs/vulnerabilities.html b/docs/vulnerabilities.html index f8b50af0..f38b40c3 100644 --- a/docs/vulnerabilities.html +++ b/docs/vulnerabilities.html @@ -5,7 +5,7 @@ Vulnerabilities | Scout - + @@ -76,7 +76,7 @@ storage limit problems.

This vulnerability again falls under the Denial of Service category and has a Medium severity.

Unsafe map get

The use of certain methods (get, get_unchecked, try_get_unchecked) on a Map object in the Soroban environment without appropriate error handling can lead to potential runtime panics. This vulnerability stems from accessing the map's values with keys that may not exist, without using safer alternatives that check the existence of the key. Such practices can compromise the robustness of the smart contract by causing it to terminate unexpectedly, which may lead to denial of service or inconsistent state within the contract.

This vulnerability falls under the Validations and error handling category category and is assigned a Medium severity level.

Zero or test address

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.

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.

This vulnerability falls under the Validations and error handling category and has a Medium severity.

Incorrect Exponentiation

It's common to use ^ for exponentiation. However in Rust, ^ is the XOR operator. If the ^ operator is used, it could lead to unexpected behavior in the contract. It's recommended to use the method pow() for exponentiation or .bitxor() for XOR operations.

This vulnerability falls under the Arithmetic category and has a Critical severity.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/assert-violation.html b/docs/vulnerabilities/assert-violation.html index 2cbe363b..c0e51bb2 100644 --- a/docs/vulnerabilities/assert-violation.html +++ b/docs/vulnerabilities/assert-violation.html @@ -5,13 +5,13 @@ Assert violation | Scout - +

Assert violation

Description

The assert! macro is used in Rust to ensure that a certain condition holds true at a certain point in your code. The assert! macro can cause the contract to panic. Therefore, the detector suggests replacing assert! constructs with Error enum structures.

Exploit Scenario

Consider the following Soroban contract:

    pub fn assert_if_greater_than_10(_env: Env, value: u128) -> bool {
assert!(value <= 10, "value should be less than 10");
true
}

The problem arises from the use of the assert! macro, if the condition is not met, the contract panics.

The vulnerable code example can be found here.

Remediation

Avoid the use of assert! macro. Instead, use a proper error and return it.

    pub fn assert_if_greater_than_10(_env: Env, value: u128) -> Result<bool, AVError> {
if value <= 10 {
Ok(true)
} else {
Err(AVError::GreaterThan10)
}
}

The remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/avoid-core-mem-forget.html b/docs/vulnerabilities/avoid-core-mem-forget.html index 2b0b7eff..0ca2e50e 100644 --- a/docs/vulnerabilities/avoid-core-mem-forget.html +++ b/docs/vulnerabilities/avoid-core-mem-forget.html @@ -5,13 +5,13 @@ Avoid core::mem::forget usage | Scout - +

Avoid core::mem::forget usage

Description

The core::mem::forget function usage is a bad practice.

Exploit Scenario

Consider the following Soroban contract:

   pub fn forget_something(n: WithoutCopy) -> u64 {
core::mem::forget(n);
0
}

The problem arises from the use of the 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.

The vulnerable code example can be found here.

Remediation

Use the pattern let _ = n; or the .drop() method instead of core::mem::forget(n);.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/avoid-panic-error.html b/docs/vulnerabilities/avoid-panic-error.html index 122dc9a7..a9060607 100644 --- a/docs/vulnerabilities/avoid-panic-error.html +++ b/docs/vulnerabilities/avoid-panic-error.html @@ -5,13 +5,13 @@ Avoid panic error | Scout - +

Avoid panic error

Description

This detector checks for the use of the panic! macro in the code. The panic! macro is used to stop execution when a condition is not met. This is useful for testing and prototyping, but should be avoided in production code.

Using Result as return type for functions that can fail is the idiomatic way to handle errors in Rust. The Result type is an enum that can be either Ok or Err. The Err variant can contain an error message. The ? operator can be used to propagate the error message to the caller.

This way, the caller can decide how to handle the error, although the state of the contract is always reverted on the callee.

Exploit Scenario

In the following example, the panic! command is being used to handle errors, disallowing the caller to handle the error in a different way, and completely stopping execution of the caller contract.

pub fn add(env: Env, value: u32) -> u32 {
let storage = env.storage().instance();
let mut count: u32 = storage.get(&COUNTER).unwrap_or(0);
match count.checked_add(value) {
Some(value) => count = value,
None => panic!("Overflow error"),
}
storage.set(&COUNTER, &count);
storage.extend_ttl(100, 100);
count
}

The add function takes a value as an argument and adds it to the value stored in the contract's storage. The function first checks if the addition will cause an overflow. If the addition will cause an overflow, the function will panic. If the addition will not cause an overflow, the function will add the value to the contract's storage.

The usage of panic! in this example, is not recommended because it will stop the execution of the caller contract. If the method was called by the user, then he will receive ContractTrapped as the only error message.

The vulnerable code example can be found here.

Remediation

A possible remediation goes as follows:

pub fn add(env: Env, value: u32) -> Result<u32, Error> {
let storage = env.storage().instance();
let mut count: u32 = storage.get(&COUNTER).unwrap_or(0);
match count.checked_add(value) {
Some(value) => count = value,
None => return Err(Error::OverflowError),
}
storage.set(&COUNTER, &count);
storage.extend_ttl(100, 100);
Ok(count)
}

And adding the following Error enum:

#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
OverflowError = 1,
}

By first defining the Error enum and then returning a Result<(), Error>, more information is added to the caller and, e.g. the caller contract could decide to revert the transaction or to continue execution.

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/avoid-unsafe-block.html b/docs/vulnerabilities/avoid-unsafe-block.html index 0aa4c4bb..d3a59f1e 100644 --- a/docs/vulnerabilities/avoid-unsafe-block.html +++ b/docs/vulnerabilities/avoid-unsafe-block.html @@ -5,13 +5,13 @@ Avoid unsafe block | Scout - +

Avoid unsafe block

Description

The use of unsafe blocks in Rust is generally discouraged due to the potential risks it poses to the safety and reliability of the code. Rust's primary appeal lies in its ability to provide memory safety guarantees, which are largely enforced through its ownership and type systems. When you enter an unsafe block, you're effectively bypassing these safety checks. This can lead to various issues, such as undefined behavior, memory leaks, or security vulnerabilities. These blocks require the programmer to manually ensure that memory is correctly managed and accessed, which is prone to human error and can be challenging even for experienced developers. Therefore, unsafe blocks should only be used when absolutely necessary and when the safety of the operations within can be assured.

Exploit Scenario

In this example we can see that it creates a raw pointer named result_ptr. Then (*result_ptr).to_bits() dereferences the raw pointer. This directly accesses the memory location and calls the to_bits method on the value stored at that location.

Raw pointers bypass Rust's type safety system and memory management features. If something goes wrong with the calculations or the value of n, dereferencing the pointer could lead to a memory access violations or undefined behavior.

#[contractimpl]
impl AvoidUnsafeBlock {
pub fn unsafe_function(n: u64) -> u64 {
unsafe {
let mut i = n as f64;
let mut y = i.to_bits();
y = 0x5fe6ec85e7de30da - (y >> 1);
i = f64::from_bits(y);
i *= 1.5 - 0.5 * n as f64 * i * i;
i *= 1.5 - 0.5 * n as f64 * i * i;

let result_ptr: *mut f64 = &mut i;

(*result_ptr).to_bits()
}
}
}

The vulnerable code example can be found here.

Remediation

By removing the raw pointer, the following version eliminates the vulnerability associated with dereferencing memory in an unsafe way. Rust's type safety checks ensure memory is accessed correctly, preventing the potential issues mentioned earlier.

#[contractimpl]
impl AvoidUnsafeBlock {
pub fn unsafe_function(n: u64) -> u64 {
let mut i = n as f64;
let mut y = i.to_bits();
y = 0x5fe6ec85e7de30da - (y >> 1);
i = f64::from_bits(y);
i *= 1.5 - 0.5 * n as f64 * i * i;
i *= 1.5 - 0.5 * n as f64 * i * i;
i.to_bits()
}
}

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/divide-before-multiply.html b/docs/vulnerabilities/divide-before-multiply.html index bf646541..dca8c542 100644 --- a/docs/vulnerabilities/divide-before-multiply.html +++ b/docs/vulnerabilities/divide-before-multiply.html @@ -5,13 +5,13 @@ Divide before multiply | Scout - +

Divide before multiply

Description

In Rust, the order of operations can influence the precision of the result, especially in integer arithmetic. Performing a division operation before a multiplication can lead to a loss of precision as division between integers might return zero. This issue can have serious consequences in programs such as smart contracts where numerical precision is critical.

Exploit Scenario

Consider the following Soroban contract:


pub fn split_profit(percentage: u64, total_profit: u64) -> u64 {
(percentage / 100) * total_profit
}

In this contract, the split_profit function divides the percentage by 100 before multiplying it with total_profit. This could lead to a loss of precision if percentage is less than 100 as the division would return 0. This could lead to incorrect calculations and potential financial loss in a real-world smart contract.

The vulnerable code example can be found here.

Remediation

Reverse the order of operations to ensure multiplication occurs before division.


pub fn split_profit(&self, percentage: u64, total_profit: u64) -> u64 {
(percentage * total_profit) / 100
}

The remediated code example can be found here.

References

Rust documentation: Integer Division

- + \ No newline at end of file diff --git a/docs/vulnerabilities/dos-unbounded-operation.html b/docs/vulnerabilities/dos-unbounded-operation.html index 2455fb95..98ff5b2a 100644 --- a/docs/vulnerabilities/dos-unbounded-operation.html +++ b/docs/vulnerabilities/dos-unbounded-operation.html @@ -5,13 +5,13 @@ DoS unbounded operation | Scout - +

DoS unbounded operation

Description

Each block in a Stellar Blockchain has an upper bound on the amount of gas that can be spent, and thus the amount computation that can be done. This is the Block Gas Limit. If the gas spent exceeds this limit, the transaction will fail.

In this smart contract a malicious user may modify the smart contract's conditions so that any transaction coming after will fail, thus imposing a denial of service for other users.

Exploit Scenario

In the following example, a contract has a function ´unsafe_loop_with_array´, which contains a for loop that iterates over a range of numbers from 0 to the lenght of the array ´unknown_array´. The issue is that if the length of the array is extremely large, it would cause the loop to execute many times, potentially leading to an unusable state of the contract.

 pub fn unsafe_loop_with_array(unknown_array: BytesN<8>) -> u32 {
let mut sum = 0;
for i in 0..unknown_array.len() {
sum += i;
}
sum
}

The vulnerable code example can be found here.

Remediation

To solve this, instead of relying on an external parameter, we should introduce a known value directly into the loop.

  pub fn safe_loop_with_array() -> u64 {
let mut sum = 0;
let known_array = [0; 8];
for i in 0..known_array.len() {
sum += i;
}
sum as u64
}

The remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/dos-unexpected-revert-with-vector.html b/docs/vulnerabilities/dos-unexpected-revert-with-vector.html index 0324bb40..e44217d8 100644 --- a/docs/vulnerabilities/dos-unexpected-revert-with-vector.html +++ b/docs/vulnerabilities/dos-unexpected-revert-with-vector.html @@ -5,7 +5,7 @@ DoS unexpected revert with vector | Scout - + @@ -26,7 +26,7 @@ getting a candidate by index, checking if an account has voted, and voting for a candidate.

In this case, we see that a vector is being used to store the array of candidates for an election. Notice how the candidates array push operation has no access control or proper storage management in the function add_candidate(), which can cause a revert if the array is full.

The vulnerable code example can be found here.

Remediation

This issue can be addressed in different ways.

On the one hand, if the amount of candidates is going to be limited, and only authorized users are going to add new candidates, then enforcing this authorization would be a sufficient fix to prevent attackers from filling the array and producing a denial of service attack.

pub fn add_candidate(env: Env, candidate: Address, caller: Address) -> Result<(), URError> {
let mut state = Self::get_state(env.clone());
// Require authorization from an admin set at contract initalization.
state.admin.require_auth();
if Self::vote_ended(env.clone()) {
return Err(URError::VoteEnded);
}
if state.already_voted.contains_key(caller.clone()) {
return Err(URError::AccountAlreadyVoted);
} else {
state.candidates.push_back(candidate.clone());
state.votes.set(candidate, 0);
Ok(())
}
}

This remediated code example can be found here.

Alternatively, if any user should be authorized to add new candidates, a different data structure should be used, one without the limitations of a vector. For example, a dictionary can be implemented in Soroban by defining a struct for the Candidate, accessible through a DataKey enum like the one we have here. This data structure does not have the storage limitations of vectors, and using it to handle new candidates would prevent the issue.

 pub fn add_candidate(env: Env, candidate: Address, caller: Address) -> Result<(), URError> {
caller.require_auth();
let mut state = Self::get_state(env.clone());
if Self::vote_ended(env.clone()) {
return Err(URError::VoteEnded);
}
if Self::account_has_voted(env.clone(), caller.clone()) {
return Err(URError::AccountAlreadyVoted);
} else {
// Replace the Vector with a mapping like structure made with a DataKey enum.
env.storage().instance().set(&DataKey::Candidate(candidate.clone()), &Candidate{votes: 0});
state.total_candidates += 1;
env.storage().instance().set(&DataKey::State, &state);
Ok(())
}
}

This remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/incorrect-exponentiation.html b/docs/vulnerabilities/incorrect-exponentiation.html index 7e4be5a5..8f2db034 100644 --- a/docs/vulnerabilities/incorrect-exponentiation.html +++ b/docs/vulnerabilities/incorrect-exponentiation.html @@ -5,13 +5,13 @@ Incorrect Exponentiation | Scout - +

Incorrect Exponentiation

Description

The operator ^ is not an exponential operator, it is a bitwise XOR. Make sure to use pow() instead for exponentiation. In case of performing a XOR operation, use .bitxor() for clarity.

Exploit Scenario

In the following example, the ^ operand is being used for exponentiation. But in Rust, ^ is the operand for an XOR operation. If misused, this could lead to unexpected behaviour in our contract.

   pub fn exp_data_3(e: Env) -> u128 {
let mut data = e.storage()
.instance()
.get::<DataKey, u128>(&DataKey::Data)
.expect("Data not found");

data ^= 3;
data
}

The vulnerable code example can be found here.

Remediation

A possible solution is to use the method pow(). But, if a XOR operation is wanted, .bitxor() method is recommended.

    pub fn exp_data_3(e: Env) -> u128 {
let data = e.storage()
.instance()
.get::<DataKey, u128>(&DataKey::Data)
.expect("Data not found");

data.pow(3)
}

The remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/insufficiently-random-values.html b/docs/vulnerabilities/insufficiently-random-values.html index 35d80854..2798c455 100644 --- a/docs/vulnerabilities/insufficiently-random-values.html +++ b/docs/vulnerabilities/insufficiently-random-values.html @@ -5,13 +5,13 @@ Insufficiently random values | Scout - +

Insufficiently random values

Description

Using block attributes like timestamp or sequence for random number generation in Soroban 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.

Exploit Scenario

Consider the following Soroban contract:


pub fn generate_random_value_timestamp(env: Env, max_val: u64) -> Result<u64, Error> {
if max_val == 0 {
Err(Error::MaxValZero)
} else {
let val = env.ledger().timestamp() % max_val;
Ok(val)
}
}

pub fn generate_random_value_sequence(env: Env, max_val: u32) -> Result<u32, Error> {
if max_val == 0 {
Err(Error::MaxValZero)
} else {
let val = env.ledger().sequence() % max_val;
Ok(val)
}
}

The vulnerability lies in these functions use of blockchain-provided data like block timestamp and sequence number for pseudo-random number generation. This reliance on predictable blockchain data makes the generated values susceptible to manipulation by attackers.

The vulnerable code example can be found here.

Remediation

Avoid using block attributes like timestamp or sequence for randomness generation, and consider using PRNG instead.


pub fn generate_random_value(env: Env, max_val: u64) -> Result<u64, Error> {
if max_val == 0 {
Err(Error::MaxValZero)
} else {
let val = env.prng().gen_range(0..max_val);
Ok(val)
}
}

The remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/iterators-over-indexing.html b/docs/vulnerabilities/iterators-over-indexing.html index 8cac5f87..49073207 100644 --- a/docs/vulnerabilities/iterators-over-indexing.html +++ b/docs/vulnerabilities/iterators-over-indexing.html @@ -5,13 +5,13 @@ Iterators over indexing | Scout - +

Iterators over indexing

Description

Iterating with hardcoded indexes is slower than using an iterator. Also, if the index is out of bounds, it will panic.

This could lead to potential integer overflow vulnerabilities, which would trigger a panic in debug builds or wrap in release mode, jeopardizing the integrity and security of the smart contract. Additionally, failing to verify the existence of data in storage before operations could result in unexpected errors or runtime failures, compromising the reliability of the contract execution.

Exploit Scenario

Consider the following Soroban contract:

     pub fn sum(e: Env) -> Result<i32, Error> {
let mut ret = 0_i32;
let vec = e
.storage()
.instance()
.get::<DataKey, Vec<i32>>(&DataKey::Data)
.ok_or(Error::NoData)?;
for i in 0..4 {
ret = ret
.checked_add(vec.get(i).ok_or(Error::NoData)?)
.ok_or(Error::IntegerOverflow)?;
}
Ok(ret)
}

The problem arises in the for loop. If vec has less than 4 elements, the contract will panic.

The vulnerable code example can be found here.

Remediation

     pub fn sum(e: Env) -> Result<i32, Error> {
let mut ret = 0_i32;
let vec = e
.storage()
.instance()
.get::<DataKey, Vec<i32>>(&DataKey::Data)
.ok_or(Error::NoData)?;
for i in vec {
ret = ret.checked_add(i).ok_or(Error::IntegerOverflow)?;
}
Ok(ret)
}

Instead of using a fixed loop, iterate through the vector itself using for i in vec. This ensures the loop iterates only for valid elements present in the vector.

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/overflow-check.html b/docs/vulnerabilities/overflow-check.html index 5946b144..cd40f69a 100644 --- a/docs/vulnerabilities/overflow-check.html +++ b/docs/vulnerabilities/overflow-check.html @@ -5,7 +5,7 @@ Overflow check | Scout - + @@ -13,7 +13,7 @@

Overflow check

Description

Checks that overflow-checks is enabled in the [profile.release] section of the Cargo.toml.

Integer overflow will trigger a panic in debug builds or will wrap in release mode. Division by zero will cause a panic in either mode. In some applications one wants explicitly checked, wrapping or saturating arithmetic.

Exploit Scenario

Consider the following Cargo.toml, in a Soroban contract:

[profile.release]
overflow-checks = false

Problems can arise if overflow-checks is disabled.

The vulnerable code example can be found here.

Remediation

[profile.release]
overflow-checks = true

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/set-contract-storage.html b/docs/vulnerabilities/set-contract-storage.html index 25624795..300d61eb 100644 --- a/docs/vulnerabilities/set-contract-storage.html +++ b/docs/vulnerabilities/set-contract-storage.html @@ -5,7 +5,7 @@ Set contract storage | Scout - + @@ -21,7 +21,7 @@ To prevent this issue, set access control and proper authorization validation for the SetContractStorage() function.

For example, the code below, ensures only the authorized users can call SetContractStorage().

#[contractimpl]
impl SetContractStorage {
/// Increment an internal counter; return the new value.
pub fn increment(env: Env, user: Address) -> u32 {
user.require_auth();
let storage = env.storage().instance();
let mut count: u32 = storage.get(&user).unwrap_or_default();
count += 1;
storage.set(&user, &count);
storage.extend_ttl(100, 100);
count
}
}

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/soroban-version.html b/docs/vulnerabilities/soroban-version.html index aef20b39..6e853abb 100644 --- a/docs/vulnerabilities/soroban-version.html +++ b/docs/vulnerabilities/soroban-version.html @@ -5,13 +5,13 @@ Soroban version | Scout - +

Soroban version

Description

Using an old version of Soroban can be dangerous, as it may have bugs or security issues. Use the latest version available.

Exploit Scenario

Consider the following Cargo.toml:

    [dependencies]
soroban-sdk = { version = "=19.0.0" }

[dev-dependencies]
soroban-sdk = { version = "=19.0.0", features = ["testutils"] }

Problems can arise if the version is not updated to the latest available.

The vulnerable code example can be found here.

Remediation

    [dependencies]
// Use the latest version available.
soroban-sdk = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }

The remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unprotected-mapping-operation.html b/docs/vulnerabilities/unprotected-mapping-operation.html index 83458859..094aa209 100644 --- a/docs/vulnerabilities/unprotected-mapping-operation.html +++ b/docs/vulnerabilities/unprotected-mapping-operation.html @@ -5,13 +5,13 @@ Unprotected mapping operation | Scout - +

Unprotected mapping operation

Description

Modifying mappings with an arbitrary key given by users can be a significant vulnerability for several reasons:

  • 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.

  • 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.

  • 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.

Exploit Scenario

Consider the following Soroban contract:

   pub fn set_balance(env: Env, address: Address, balance: i128) -> State {
// Get the current state.
let mut state = Self::get_state(env.clone());

// Set the new account to have total supply if it doesn't exist.
if !state.balances.contains_key(address.clone()) {
state.balances.set(address, balance);
// Save the state.
env.storage().persistent().set(&STATE, &state);
}

state
}

The set_balance() function allows anyone to call it and modify the account balances in the state. It lacks authorization checks and allows modifying the mutable state directly.

The vulnerable code example can be found here.

Remediation

The fix adds an address.require_auth() step, likely checking user permissions to update balances. This ensures only authorized users can modify account data.

    pub fn set_balance(env: Env, address: Address, balance: i128) -> State {
// Authenticate user
address.require_auth();

// Get the current state.
let mut state = Self::get_state(env.clone());

// Set the new account to have total supply if it doesn't exist.
if !state.balances.contains_key(address.clone()) {
state.balances.set(address, balance);
// Save the state.
env.storage().persistent().set(&STATE, &state);
}

state
}

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unprotected-update-current-contract-wasm.html b/docs/vulnerabilities/unprotected-update-current-contract-wasm.html index 87f4620f..aa858fd5 100644 --- a/docs/vulnerabilities/unprotected-update-current-contract-wasm.html +++ b/docs/vulnerabilities/unprotected-update-current-contract-wasm.html @@ -5,13 +5,13 @@ Unprotected update current contract wasm | Scout - +

Unprotected update current contract wasm

Description

It warns you if update_current_contract_wasm() function is called without a previous check of the address of the caller. If users are allowed to call update_current_contract_wasm(), 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.

Exploit Scenario

Consider the following Soroban contract:

#[contractimpl]
impl UpgradeableContract {
pub fn init(e: Env, admin: Address) {
e.storage().instance().set(&DataKey::Admin, &admin);
}

pub fn version() -> u32 {
1
}

pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) {
e.deployer().update_current_contract_wasm(new_wasm_hash);
}
}

This contract allows upgrades through the update_current_contract_wasm function. If just anyone can call this function, they could modify the contract behaviour.

The vulnerable code example can be found here.

Remediation

To prevent this, the function should be restricted to administrators or authorized users only.

#[contractimpl]
impl UpgradeableContract {
pub fn init(e: Env, admin: Address) {
e.storage().instance().set(&DataKey::Admin, &admin);
}

pub fn version() -> u32 {
1
}

pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) {
let admin: Address = e.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();

e.deployer().update_current_contract_wasm(new_wasm_hash);
}
}

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unrestricted-transfer-from.html b/docs/vulnerabilities/unrestricted-transfer-from.html index bc329330..29ec8d29 100644 --- a/docs/vulnerabilities/unrestricted-transfer-from.html +++ b/docs/vulnerabilities/unrestricted-transfer-from.html @@ -5,13 +5,13 @@ Unrestricted Transfer From | Scout - +

Unrestricted Transfer From

Description

Allowing unrestricted transfer_from operations poses a significant vulnerability. When 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.

Exploit Scenario

Consider the following Soroban function:

     pub fn deposit(env: Env, from: Address) -> Result<(), UTFError> {
let mut state: State = Self::get_state(env.clone())?;
state.buyer.require_auth();
if state.status != Status::Created {
return Err(UTFError::StatusMustBeCreated);
}
let token_client = token::Client::new(&env, &state.token);
token_client.transfer_from(
&env.current_contract_address(),
&from,
&env.current_contract_address(),
&state.amount,
);
state.status = Status::Locked;
env.storage().instance().set(&STATE, &state);
Ok(())
}

The vulnerability in this deposit function arises from the use of from, an user-defined parameter as an argument in the from field of the transfer_from function. Alice can approve a contract to spend their tokens, then Bob can call that contract, use that allowance to send as themselves Alice's tokens.

The vulnerable code example can be found here.

Remediation

Avoid using user-defined arguments as from parameter in transfer_from. Instead, use state.buyer as shown in the following example.

     pub fn deposit(env: Env) -> Result<(), UTFError> {
let mut state: State = Self::get_state(env.clone())?;
state.buyer.require_auth();
if state.status != Status::Created {
return Err(UTFError::StatusMustBeCreated);
}
let token_client = token::Client::new(&env, &state.token);
token_client.transfer_from(
&env.current_contract_address(),
&state.buyer,
&env.current_contract_address(),
&state.amount,
);
state.status = Status::Locked;
env.storage().instance().set(&STATE, &state);
Ok(())
}

The remediated code example can be found here.

References

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unsafe-expect.html b/docs/vulnerabilities/unsafe-expect.html index 5068529b..2aaa535f 100644 --- a/docs/vulnerabilities/unsafe-expect.html +++ b/docs/vulnerabilities/unsafe-expect.html @@ -5,13 +5,13 @@ Unsafe expect | Scout - +

Unsafe expect

Description

In Rust, the expect method is often used for error handling. It returns the contained Ok value for a Result or Some value for an Option. If an error occurs, it calls panic! with a provided error message.

The usage of expect can lead to a panic and crash the program, which is not desired behavior in most cases, especially for a smart contract.

Exploit Scenario

Consider the following Soroban contract:


pub fn balance_of(env: Env, owner: Address) -> i128 {
let state = Self::get_state(env);
state.balances.get(owner).expect("could not get balance")
}

In this contract, the balance_of function uses the expect method to retrieve the balance of an account. If there is no entry for this account in the balances mapping, the contract will panic and halt execution, which could be exploited maliciously to disrupt the contract's operation.

The vulnerable code example can be found here.

Remediation

Instead of using expect, use a safer method for error handling. In this case, if there is no entry for an account in the balances mapping, return a default value (like 0).


pub fn balance_of(env: Env, owner: Address) -> i128 {
let state = Self::get_state(env);
state.balances.get(owner).unwrap_or(0)
}

The remediated code example can be found here.

References

Rust documentation: expect

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unsafe-map-get.html b/docs/vulnerabilities/unsafe-map-get.html index de4d8296..bec13cbc 100644 --- a/docs/vulnerabilities/unsafe-map-get.html +++ b/docs/vulnerabilities/unsafe-map-get.html @@ -5,13 +5,13 @@ Unsafe map get | Scout - +

Unsafe map get

Description

The use of certain methods (get, get_unchecked, try_get_unchecked) on a Map object in the Soroban environment without appropriate error handling can lead to potential runtime panics. This vulnerability stems from accessing the map's values with keys that may not exist, without using safer alternatives that check the existence of the key. Such practices can compromise the robustness of the smart contract by causing it to terminate unexpectedly, which may lead to denial of service or inconsistent state within the contract.

Exploit Scenario

Consider the following Soroban contract:

    #[contractimpl]
impl UnsafeMapGet {
pub fn get_from_map(env: Env) -> Option<i32> {
let map: Map<Val, Val> = map![&env, (1i32.into_val(&env), 2i64.into_val(&env))];
let map: Val = map.into();
let map: Map<i32, i32> = map.try_into_val(&env).unwrap();
map.get(1)
}
}

This function retrieves values from a map using map.get() without checking if the key actually exists in the map. If the key doesn't exist after the conversion, get will panic, causing the entire contract to fail.

The vulnerable code example can be found here.

Remediation

Both remediated functions presented below ensure the contract doesn't panic due to missing keys. The remediated contract avoid the unsafe map get vulnerability by using try_get for safer access and ensuring the map keys and values have compatible types throughout the process.

    #[contractimpl]
impl UnsafeMapGet {
pub fn get_map_with_different_values(env: Env, key: i32) -> Result<Option<i32>, Error> {
let map: Map<Val, Val> = map![
&env,
(1i32.into_val(&env), 2i32.into_val(&env)),
(3i32.into_val(&env), 4i64.into_val(&env)),
];
let map: Val = map.into();
let map: Map<i32, i32> = map.try_into_val(&env).unwrap();
map.try_get(key).map_err(Error::from)
}

pub fn get_map_with_different_keys(env: Env, key: i32) -> Result<Option<i32>, Error> {
let map: Map<Val, Val> = map![
&env,
(1i32.into_val(&env), 2i32.into_val(&env)),
(3i64.into_val(&env), 4i32.into_val(&env)),
];
let map: Val = map.into();
let map: Map<i32, i32> = map.try_into_val(&env).unwrap();
map.try_get(key).map_err(Error::from)
}
}

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unsafe-unwrap.html b/docs/vulnerabilities/unsafe-unwrap.html index 218b4309..4e1d074d 100644 --- a/docs/vulnerabilities/unsafe-unwrap.html +++ b/docs/vulnerabilities/unsafe-unwrap.html @@ -5,13 +5,13 @@ Unsafe unwrap | Scout - +

Unsafe unwrap

Description

In Rust, the unwrap method is commonly used for error handling. It retrieves the inner value of an Option or Result. If an error or None occurs, it calls panic! without a custom error message.

The usage of unwrap can lead to a panic and crash the program, which is not desired behavior in most cases, particularly in smart contracts.

Exploit Scenario

Consider the following Soroban contract:

#[contractimpl]
impl UnsafeUnwrap {
pub fn unwrap(n: u64) -> u64 {
let result = Self::non_zero_or_error(n);
result.unwrap()
}

pub fn non_zero_or_error(n: u64) -> Result<u64, Error> {
if n == 0 {
return Err(Error::CustomError);
}
Ok(n)
}
}

In this contract, the unwrap function uses the unwrap method to save the result of the non_zero_or_error function. If the function returns Err, the contract will panic and halt execution, potentially leading to malicious exploitation to disrupt the contract's operation.

The vulnerable code example can be found here.

Remediation

Instead of using unwrap, use a safer method for error handling. In this case, if the function returns Err, it will return a default value (like 0).

#[contractimpl]
impl UnsafeUnwrap {
pub fn unwrap_or_default(n: u64) -> u64 {
let result = Self::non_zero_or_error(n);
result.unwrap_or(0)
}

pub fn non_zero_or_error(n: u64) -> Result<u64, Error> {
if n == 0 {
return Err(Error::CustomError);
}
Ok(n)
}
}

The remediated code example can be found here.

References

Rust documentation: unwrap

- + \ No newline at end of file diff --git a/docs/vulnerabilities/unused-return-enum.html b/docs/vulnerabilities/unused-return-enum.html index aa1fd057..6130ba6e 100644 --- a/docs/vulnerabilities/unused-return-enum.html +++ b/docs/vulnerabilities/unused-return-enum.html @@ -5,7 +5,7 @@ Unused return enum | Scout - + @@ -19,7 +19,7 @@ when the percentage difference is calculated successfully. By providing a check in the linter that ensures that all the variants of the Result enum are used, this bug could have been avoided. This is shown in the example below:

#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
/// An overflow was produced.
Overflow = 1,
}


pub fn get_percentage_difference(balance1: u128, balance2: u128) -> Result<u128, Error> {
let absolute_difference = balance1.abs_diff(balance2);
let sum = balance1 + balance2;

match 100u128.checked_mul(absolute_difference / sum) {
Some(result) => Ok(result),
None => Err(Error::Overflow),
}
}

The remediated code example can be found here.

- + \ No newline at end of file diff --git a/index.html b/index.html index 5fdbc0e4..4fc75ec6 100644 --- a/index.html +++ b/index.html @@ -5,13 +5,13 @@ Scout - +

Scout

Security Analysis Tool

The Tool

Scout is an extensible open-source tool intended to assist Soroban smart contract developers and auditors detect common security issues and deviations from best practices.

Security

This tool will help developers write secure and more robust smart contracts. Our interest in this project comes from our experience in manual auditing and our usage of comparable tools in other blockchains.

Research

To improve coverage and precision, we persist in research efforts on static and dynamic analysis techniques. Find more about our ongoing research at our associated repository.

- + \ No newline at end of file diff --git a/markdown-page.html b/markdown-page.html index 176066e3..00f283d4 100644 --- a/markdown-page.html +++ b/markdown-page.html @@ -5,13 +5,13 @@ Markdown page example | Scout - +

Markdown page example

You don't need React to write simple standalone pages.

- + \ No newline at end of file