<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mohamed AboElKheir</title>
    <description>The latest articles on DEV Community by Mohamed AboElKheir (@mohamed_aboelkheir).</description>
    <link>https://dev.to/mohamed_aboelkheir</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1826313%2F9cc985ba-3c41-40e6-9f1a-ee048bcb4c99.jpg</url>
      <title>DEV Community: Mohamed AboElKheir</title>
      <link>https://dev.to/mohamed_aboelkheir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohamed_aboelkheir"/>
    <language>en</language>
    <item>
      <title>How Reachability Analysis 🔎 can help with open source vulnerabilities mess (Coana as an example)</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Wed, 22 Jan 2025 17:30:13 +0000</pubDate>
      <link>https://dev.to/mohamed_aboelkheir/how-reachability-analysis-can-help-with-open-source-vulnerabilities-mess-coana-as-an-example-1dh1</link>
      <guid>https://dev.to/mohamed_aboelkheir/how-reachability-analysis-can-help-with-open-source-vulnerabilities-mess-coana-as-an-example-1dh1</guid>
      <description>&lt;p&gt;If you are a security engineer or a developer, you probably already know the pain of having to deal with the vulnerabilities affecting the open-source packages (e.g. npm, pip, maven, .. etc) used by your application. In today's story, we discuss "Reachability analysis", a feature that promises to ease this pain by eliminating 70-80% of these alerts using &lt;a href="https://www.coana.tech/" rel="noopener noreferrer"&gt;Coana&lt;/a&gt;,  as an example. But first, let's dig into what is wrong with pen-source vulnerability scanning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The open-source vulnerabilities mess
&lt;/h2&gt;

&lt;p&gt;Software is built using many &lt;a href="https://medium.com/appsec-untangled/appsec-tip-1-know-your-building-blocks-d58ed08743a7" rel="noopener noreferrer"&gt;building blocks&lt;/a&gt;, and for any application to be secure all the building blocks need to be secure. One of the most important building blocks is open-source packages (e.g. npm, pip, maven, .. etc) which is estimated to constitute 70-90% of modern applications according to some &lt;a href="https://www.linuxfoundation.org/blog/blog/a-summary-of-census-ii-open-source-software-application-libraries-the-world-depends-on" rel="noopener noreferrer"&gt;studies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That is why it is important to continuously scan open source packages to check if the used versions are affected by known vulnerabilities, as these vulnerabilities could potentially lead to exploits even if the code of the application doesn't have any security issues.&lt;/p&gt;

&lt;p&gt;Most organizations use SCA tools (e.g. &lt;a href="https://snyk.io/product/open-source-security-management/" rel="noopener noreferrer"&gt;Snyk&lt;/a&gt;, &lt;a href="https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt;, .. etc) to perform such scans, However, they usually run into multiple issues in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of findings is huge and unmanageable, this is mainly because as mentioned earlier open source can be up to 90% of the actual codebase (e.g. a simple hello world application that uses &lt;a href="https://www.npmjs.com/package/express" rel="noopener noreferrer"&gt;Express&lt;/a&gt; can have more than 100 npm packages if you count the child dependencies as shown below, and this grows pretty quickly as the application gets more complex).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;package.json
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"test"&lt;/span&gt;,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;,
  &lt;span class="s2"&gt;"main"&lt;/span&gt;: &lt;span class="s2"&gt;"index.js"&lt;/span&gt;,
  &lt;span class="s2"&gt;"scripts"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"test"&lt;/span&gt;: &lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;Error: no &lt;span class="nb"&gt;test &lt;/span&gt;specified&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;" &amp;amp;&amp;amp; exit 1"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"author"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
  &lt;span class="s2"&gt;"license"&lt;/span&gt;: &lt;span class="s2"&gt;"ISC"&lt;/span&gt;,
  &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
  &lt;span class="s2"&gt;"dependencies"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"escape-html"&lt;/span&gt;: &lt;span class="s2"&gt;"^1.0.3"&lt;/span&gt;,
    &lt;span class="s2"&gt;"express"&lt;/span&gt;: &lt;span class="s2"&gt;"^4.19.2"&lt;/span&gt;,
    &lt;span class="s2"&gt;"lodash"&lt;/span&gt;: &lt;span class="s2"&gt;"^4.17.20"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;npm list &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt;@1.0.0
├── escape-html@1.0.3
├─┬ express@4.19.2
│ ├─┬ accepts@1.3.8
│ │ ├─┬ mime-types@2.1.35
│ │ │ └── mime-db@1.52.0
│ │ └── negotiator@0.6.3
...

&lt;span class="c"&gt;# This simple application has 120 npm packages&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm list &lt;span class="nt"&gt;--all&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
     120

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Fixing the vulnerabilities is not as straightforward as it seems for multiple reasons, e.g.:

&lt;ol&gt;
&lt;li&gt;The package with the vulnerability may not have a patched version yet.&lt;/li&gt;
&lt;li&gt;The vulnerability affects a child package (dependency of a dependency), and the parent package doesn't have a version that uses the patched version of the child package yet.&lt;/li&gt;
&lt;li&gt;The patched version of the affected package could introduce some breaking changes that need some code refactoring.&lt;/li&gt;
&lt;li&gt;Even if all these issues are not present, in many cases the application usually doesn't have enough testing coverage, which means manual testing is needed to apply the fix.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;These issues, along with the huge number of findings, put the development team in front of a difficult choice: either spend an unreasonable amount of time fixing and testing the findings, slowing down the development process or ignore the findings (or fix them in batches in long periods of time), which is what many teams end up doing.&lt;/li&gt;
&lt;li&gt;As a result of this mess, some teams decide to only prioritize findings of high or critical severities, but this is usually not enough to bring down the load to a reasonable level.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Severity is not everything
&lt;/h2&gt;

&lt;p&gt;Now, let's look at things from a different angle. A developer could think that if my application has 100+ critical and high open source vulnerabilities, but hasn't been hacked yet, this probably means that the severity of these vulnerabilities is not really critical or high, and they wouldn't be entirely wrong.&lt;/p&gt;

&lt;p&gt;This discrepancy between finding severity and actual impact stems from the fact that open-source vulnerability scanners don't answer the question "Are these vulnerabilities exploitable in the context of my application?". As we are going to see shortly the answer for the vast majority of findings is "no", and this means two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We spend a lot of time fixing and testing vulnerabilities classified as Critical or High, but they are actually not exploitable, so they don't have any real impact.&lt;/li&gt;
&lt;li&gt;We don't know which of these vulnerabilities are actually exploitable, which means that they may end up being ignored or at least the fix would take too long, which significantly increases the risk for our application.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Is this vulnerability exploitable?
&lt;/h2&gt;

&lt;p&gt;Let's take the below application as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Sample data&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Charlie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Middleware to parse JSON bodies&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Route to get all users&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Route to get a user by id&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Route to add a new user&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Use lodash to assign an incremental id and add to users list&lt;/span&gt;
    &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maxBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Route to update a user&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Use lodash to merge the updated data with the existing user data&lt;/span&gt;
        &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userIndex&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;updatedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userIndex&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Route to delete a user&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User deleted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Start the server&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server running at &amp;lt;http://localhost&amp;gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application has two main dependencies &lt;code&gt;express&lt;/code&gt; and &lt;code&gt;lodash&lt;/code&gt;  (a very popular npm package with a variety of useful functions). In this case, we are using &lt;code&gt;lodash&lt;/code&gt; to query and update the mock user database.&lt;/p&gt;

&lt;p&gt;Let's assume this uses the below versions of these 2 packages as shown in the below &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;Error:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;specified\\&lt;/span&gt;&lt;span class="s2"&gt;" &amp;amp;&amp;amp; exit 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.19.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lodash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.17.20"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run the Snyk scanner on this application to check the findings&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;snyk &lt;span class="nb"&gt;test

&lt;/span&gt;Tested 66 dependencies &lt;span class="k"&gt;for &lt;/span&gt;known issues, found 3 issues, 3 vulnerable paths.

Issues to fix by upgrading:

  Upgrade express@4.21.1 to express@4.21.2 to fix
  ✗ Regular Expression Denial of Service &lt;span class="o"&gt;(&lt;/span&gt;ReDoS&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Medium Severity][&amp;lt;https://security.snyk.io/vuln/SNYK-JS-PATHTOREGEXP-8482416&amp;gt;] &lt;span class="k"&gt;in &lt;/span&gt;path-to-regexp@0.1.10
    introduced by express@4.21.1 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; path-to-regexp@0.1.10

  Upgrade lodash@4.17.20 to lodash@4.17.21 to fix
  ✗ Regular Expression Denial of Service &lt;span class="o"&gt;(&lt;/span&gt;ReDoS&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Medium Severity][&amp;lt;https://security.snyk.io/vuln/SNYK-JS-LODASH-1018905&amp;gt;] &lt;span class="k"&gt;in &lt;/span&gt;lodash@4.17.20
    introduced by lodash@4.17.20
  ✗ Code Injection &lt;span class="o"&gt;[&lt;/span&gt;High Severity][&amp;lt;https://security.snyk.io/vuln/SNYK-JS-LODASH-1040724&amp;gt;] &lt;span class="k"&gt;in &lt;/span&gt;lodash@4.17.20
    introduced by lodash@4.17.20

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's focus on the High severity finding &lt;a href="https://www.cve.org/CVERecord?id=CVE-2021-23337" rel="noopener noreferrer"&gt;CVE-2021-23337&lt;/a&gt; affecting the &lt;code&gt;lodash&lt;/code&gt; package (&lt;code&gt;Code Injection [High Severity][&amp;lt;https://security.snyk.io/vuln/SNYK-JS-LODASH-1040724&amp;gt;] in lodash@4.17.20&lt;/code&gt;). If this is really a “High” severity vulnerability that causes &lt;a href="https://owasp.org/www-community/attacks/Code_Injection" rel="noopener noreferrer"&gt;Code Injection&lt;/a&gt;, this means that developers should leave everything and fix this as soon as possible. However, as security engineers, it is part of our job before asking the developers to leave everything to be sure the issue actually needs such urgent action.&lt;/p&gt;

&lt;p&gt;This takes us to the question we need to answer "Is this vulnerability exploitable in the context of my application?". To be able to answer this question, let's have a closer look at the vulnerability. If we open the link mentioned in the Snyk scan &lt;a href="https://security.snyk.io/vuln/SNYK-JS-LODASH-1040724" rel="noopener noreferrer"&gt;https://security.snyk.io/vuln/SNYK-JS-LODASH-1040724&lt;/a&gt; we will find the PoC (Proof of concept) code showing the payload to exploit the vulnerability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;){console.log(process.env)}; with(obj&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this PoC and from the overview, it is clear that this payload works when passed to the &lt;code&gt;template()&lt;/code&gt; function and specifically to the &lt;code&gt;templateOptions.variable&lt;/code&gt; argument. Having a quick look at our code, we can easily see that we are not using the &lt;code&gt;template()&lt;/code&gt; function anywhere (we are only using the &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;maxBy&lt;/code&gt;, &lt;code&gt;concat&lt;/code&gt;, &lt;code&gt;findIndex&lt;/code&gt;, &lt;code&gt;merge&lt;/code&gt;, and &lt;code&gt;remove&lt;/code&gt; lodash functions ). This means that this vulnerability although initially classified as "High" is not exploitable in our case, and hence can be safely ignored/de-prioritized, and definitely we shouldn't be asking developers to leave everything to fix this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reachability analysis
&lt;/h2&gt;

&lt;p&gt;The above was an example of how we can manually review and triage a finding to determine whether it is exploitable. However, this doesn't scale well as the number of findings and the application complexity increase,  it is not feasible to perform the same analysis for hundreds of findings. This is where automation could come to the rescue!&lt;/p&gt;

&lt;p&gt;In the above example the answer to the question "Is the finding exploitable?" depended on another question "Is the vulnerable function used?". This question is easier to answer as we will see shortly, and this is basically what "Reachability" is about. If the vulnerable function is used then the vulnerability is considered "Reachable", otherwise it is not.&lt;/p&gt;

&lt;p&gt;For example, in the below application packages 2 and 3 have vulnerabilities. However, as the application only uses the vulnerable function in package 2, only package 2's vulnerability is reachable, and package 3's vulnerability is unreachable, and can be safely ignored/de-prioritized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8744xjfarlplja58kdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8744xjfarlplja58kdc.png" alt="Reachability Analysis" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Coana as an example
&lt;/h2&gt;

&lt;p&gt;Let's take &lt;a href="https://www.coana.tech/" rel="noopener noreferrer"&gt;Coana&lt;/a&gt; as an example, an SCA that performs Reachability analysis, and at the time of writing this article is &lt;a href="https://www.coana.tech/pricing" rel="noopener noreferrer"&gt;free&lt;/a&gt; to use on open-source projects. Coana creates a &lt;a href="https://en.wikipedia.org/wiki/Code_property_graph" rel="noopener noreferrer"&gt;code property graph&lt;/a&gt; of your application and uses this graph to determine which functions in your dependencies (and child dependencies) are actually being called, hence automating the analysis we performed earlier.&lt;/p&gt;

&lt;p&gt;Let's run a Coana scan on the sample application we analyzed earlier by following the steps in their &lt;a href="https://docs.coana.tech/scanning#running-a-scan" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.  As shown below, it reached the same conclusion about the &lt;code&gt;lodash&lt;/code&gt; Code injection vulnerability that it is not reachable, and in the analysis details you can see the vulnerable functions it was looking for to determine reachability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftn1xjn35rq4qf4h3c91j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftn1xjn35rq4qf4h3c91j.png" alt="Not Reachable" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foa4dw2vpd3u1097pep6d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foa4dw2vpd3u1097pep6d.png" alt="Vulnerable function not used" width="656" height="879"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's try it for an exploitable vulnerability
&lt;/h2&gt;

&lt;p&gt;Let's try that again but with the below application where the same &lt;code&gt;lodash&lt;/code&gt; vulnerability is actually exploitable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/generate-story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;h2&amp;gt;Here is your random story:&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;%= name %&amp;gt; went to &amp;lt;%= place %&amp;gt; in their &amp;lt;%= car %&amp;gt; for a nice &amp;lt;%= meal %&amp;gt;.&amp;lt;/p&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;templateOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Parse the hidden options field (this is the vulnerable part)&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;templateOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid options JSON&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// compile template without user-supplied options&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;templateOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error generating story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on &amp;lt;http://localhost:3000&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code uses the &lt;code&gt;template()&lt;/code&gt; function and also takes the &lt;code&gt;templateOptions&lt;/code&gt; from the request body of the &lt;code&gt;/generate-stroy&lt;/code&gt; route. Hence, can exploited with the payload in the poc, e.g. we can inject code to expose all environmental variables as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &amp;lt;http://localhost:3000/generate-story&amp;gt;
      &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt;
      &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"name=Alice"&lt;/span&gt;
      &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"meal=Pizza"&lt;/span&gt;
      &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"place=New York"&lt;/span&gt;
      &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"car=Tesla"&lt;/span&gt;
      &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s2"&gt;"options={&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;variable&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;":&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;&lt;span class="k"&gt;return &lt;/span&gt;JSON.stringify&lt;span class="o"&gt;(&lt;/span&gt;process.env&lt;span class="o"&gt;)}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; with&lt;span class="o"&gt;(&lt;/span&gt;obj&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;"}"&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"CLICOLOR"&lt;/span&gt;: &lt;span class="s2"&gt;"1"&lt;/span&gt;,
  &lt;span class="s2"&gt;"COLORFGBG"&lt;/span&gt;: &lt;span class="s2"&gt;"7;0"&lt;/span&gt;,
  &lt;span class="s2"&gt;"COLORTERM"&lt;/span&gt;: &lt;span class="s2"&gt;"truecolor"&lt;/span&gt;,
  &lt;span class="s2"&gt;"COMMAND_MODE"&lt;/span&gt;: &lt;span class="s2"&gt;"unix2003"&lt;/span&gt;,
  &lt;span class="s2"&gt;"EDITOR"&lt;/span&gt;: &lt;span class="s2"&gt;"vim"&lt;/span&gt;,
  &lt;span class="s2"&gt;"HISTFILESIZE"&lt;/span&gt;: &lt;span class="s2"&gt;"2000000"&lt;/span&gt;,
  &lt;span class="s2"&gt;"HISTSIZE"&lt;/span&gt;: &lt;span class="s2"&gt;"1000000"&lt;/span&gt;,
  &lt;span class="s2"&gt;"HISTTIMEFORMAT"&lt;/span&gt;: &lt;span class="s2"&gt;"%F %T "&lt;/span&gt;,
  ....
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's use Coana to scan this vulnerable example, and as expected, now the same vulnerability is shown as "Reachable". Moreover, Coana will show us the lines of code where the vulnerable function is being used, this would help us plan and test the fix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhlt30hlijacedtbd7hc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhlt30hlijacedtbd7hc.png" alt="Reachable" width="800" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngzb5amec05byazxrvl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngzb5amec05byazxrvl9.png" alt="Vulnerable function used" width="657" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reachability analysis in practice
&lt;/h2&gt;

&lt;p&gt;In practice, Coana's reachability analysis is usually able to discard 70-80% of the vulnerabilities as unreachable, this significantly reduces the load on security and development teams and also helps the same teams focus on the vulnerabilities that are more likely to have actual impact on the application. This removes a lot of the mess we explained earlier in this story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reachable != Exploitable
&lt;/h2&gt;

&lt;p&gt;One thing to note is that if a finding is reachable, it doesn't necessarily mean it is exploitable, as sometimes there other conditions that need to be met for the exploitation besides the vulnerable function being called.&lt;/p&gt;

&lt;p&gt;For example, in the vulnerable code we used above, besides using the &lt;code&gt;template()&lt;/code&gt; function, the &lt;code&gt;templateOptions&lt;/code&gt; argument needed to be controlled by user input and passed to the function. Hence, If we removed the &lt;code&gt;options&lt;/code&gt; parameter from the request body in the example, and didn't pass it to &lt;code&gt;template()&lt;/code&gt; the example no longer becomes exploitable. In this case, Coana will still mark the finding as "Reachable", and manual triage is needed to complete the analysis and decide that it is not exploitable.&lt;/p&gt;

&lt;p&gt;The below code is an updated version where the vulnerability is "Reachable" but not "Exploitable"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/generate-story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;h2&amp;gt;Here is your random story:&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;%= name %&amp;gt; went to &amp;lt;%= place %&amp;gt; in their &amp;lt;%= car %&amp;gt; for a nice &amp;lt;%= meal %&amp;gt;.&amp;lt;/p&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// compile template without user-supplied options&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meal&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;story&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error generating story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That being said, Reachability analysis still adds a lot of value by excluding the unreachable findings. The point here, is that some manual triage could help discard even more vulnerabilities that won't have impact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4dft38ycdfi3sqn7wlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4dft38ycdfi3sqn7wlt.png" alt="Reachable != Exploitable" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When using security tools such as SCA scanners, it is important to remember the initial goal we are using the tool for, which is eliminating risk and preventing negative impact on your application. Hence, it doesn't matter the number of findings we are getting from these tools, if we don't have enough confidence in the quality of these findings, and how much they actually represent risk and impact. That is why features like reachability analysis are useful, as they are able to eliminate a lot of the noise and give us much more confidence that the findings we are focusing on are the ones that represent probable risk and impact.&lt;/p&gt;

</description>
      <category>appsec</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>design</category>
    </item>
    <item>
      <title>Lessons Learned #4: One error message could expose all your data (FileSender CVE-2024–45186)</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Mon, 20 Jan 2025 14:48:54 +0000</pubDate>
      <link>https://dev.to/mohamed_aboelkheir/lessons-learned-4-one-error-message-could-expose-all-your-data-filesender-cve-2024-45186-11pf</link>
      <guid>https://dev.to/mohamed_aboelkheir/lessons-learned-4-one-error-message-could-expose-all-your-data-filesender-cve-2024-45186-11pf</guid>
      <description>&lt;p&gt;Welcome to another story in the “&lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/lessons-learned-d64346d08a74" rel="noopener noreferrer"&gt;Lessons Learned&lt;/a&gt;” series where we discuss real-world vulnerabilities from the perspective of an application security engineer focusing on the underlying root causes and the measures we can take to prevent similar issues in our applications.&lt;/p&gt;

&lt;p&gt;In today’s story, we discuss a write-up by the security researcher  &lt;a href="https://medium.com/@jonathanbouman" rel="noopener noreferrer"&gt;Jonathan Bouman&lt;/a&gt;  showing how an SSTI (Server-side template injection) vulnerability affecting an error message was used to expose all user data of an application. You can view the full write-up  &lt;a href="https://medium.com/@jonathanbouman/cve-2024-45186-unauthenticated-ssti-bug-in-filesender-exposes-mysql-s3-credentials-and-other-463a9efc1478" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Affected Application
&lt;/h2&gt;

&lt;p&gt;Today’s vulnerability affected  &lt;a href="https://github.com/filesender/filesender" rel="noopener noreferrer"&gt;FileSender&lt;/a&gt;, a popular open-source application mainly used for file-sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact of the vulnerability
&lt;/h2&gt;

&lt;p&gt;Exposure of credentials (database and S3) potentially allowing an unauthenticated user to access and tamper with all customer files managed by the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  The security researcher explored the  &lt;code&gt;/download.php&lt;/code&gt;  route which allows unauthenticated users to download files (FileSender allows authenticated users to share download links with unauthenticated users), and found that if the download link is expired the HTTP request is redirected to another unauthenticated route like  &lt;code&gt;/?s=exception&amp;amp;exception=eyJtZXNzYWdlIjoidHJhbnNmZXJfcHJlc3VtZWRfZXhwaXJlZCIsInVpZCI6IjY3MGE4N2M0YmQ0ODAiLCJkZXRhaWxzIjpudWxsfQ==&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqu37yj5bvkw95ng05ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqu37yj5bvkw95ng05ks.png" alt="Unauthenticated route" width="720" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Obviously, the  &lt;code&gt;exception&lt;/code&gt;  query parameter is base64 encoded, which implies the backend decodes this and uses it to apply some logic, specifically to generate the error message and display it as shown below:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8pl33cuo88mm8ke490f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8pl33cuo88mm8ke490f.png" alt="Error message" width="720" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  When decoding the message we get the below JSON
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo "eyJtZXNzYWdlIjoidHJhbnNmZXJfcHJlc3VtZWRfZXhwaXJlZCIsInVpZCI6IjY3MGE4N2M0YmQ0ODAiLCJkZXRhaWxzIjpudWxsfQ==" | base64 -d  | jq  

{  
  "message": "transfer_presumed_expired",  
  "uid": "670a87c4bd480",  
  "details": null  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  When changing the message in the JSON to something like  &lt;code&gt;xxxxx&lt;/code&gt;, re-encoding, and calling the same route, the new message was shown between curly brackets instead of the original message, e.g. encoding the below JSON to give a new base64 string, then replacing the value of the  &lt;code&gt;exception&lt;/code&gt;  query parameter with this new base64 string shows the error message  &lt;code&gt;{xxxxx}&lt;/code&gt;  as shown below.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo '{"message":"xxxxx","uid":"670a87c4bd480","details":null}' | base64  
eyJtZXNzYWdlIjoieHh4eHgiLCJ1aWQiOiI2NzBhODdjNGJkNDgwIiwiZGV0YWlscyI6bnVsbH0K
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtybcfadfw7iotod75sf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtybcfadfw7iotod75sf.png" alt="Modified error message" width="720" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend logic
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  When checking the code of the application to understand the logic of how the backend decodes and handles the  &lt;code&gt;exception&lt;/code&gt;  query parameter, the security researcher came across the below function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0asnrocd9mbujbm96hdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0asnrocd9mbujbm96hdb.png" alt="Backend function to decode" width="720" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  As shown this  &lt;code&gt;unserialize&lt;/code&gt;  function starts with decoding the base64 string, and then extracting the fields from the JSON (line 362).&lt;/li&gt;
&lt;li&gt;  Then it replaces any  &lt;code&gt;{cfg:some_option}&lt;/code&gt;  ,  &lt;code&gt;{conf:some_option}&lt;/code&gt;  , or  &lt;code&gt;{config:some_option}&lt;/code&gt;  values in the  &lt;code&gt;exception&lt;/code&gt;  with an empty string (line 365).&lt;/li&gt;
&lt;li&gt;  This suggests that these  &lt;code&gt;cfg/conf/config&lt;/code&gt;  values would be parsed by a templating engine and substituted with the value of the config option, and it seems the goal of line 365 is to prevent this from happening.&lt;/li&gt;
&lt;li&gt;  However, this only replaces these  &lt;code&gt;cfg/conf/config&lt;/code&gt;  values if they are enclosed in curly brackets (e.g.  &lt;code&gt;{cfg:some_option}&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Exploit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  The security researcher checked the  &lt;a href="https://github.com/filesender/filesender/blob/5c00bf7605ac7a24800bc92c78cd97eca45be5de/docs/v2.0/admin/configuration/index.md" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;  of FileSender and found some potentially sensitive configuration options such as  &lt;code&gt;db_username&lt;/code&gt;  and  &lt;code&gt;db_password&lt;/code&gt;  which hold the database credentials and also  &lt;code&gt;cloud_s3_key&lt;/code&gt;  and  &lt;code&gt;cloud_d3_secret&lt;/code&gt;  which hold the AWS credentials that are used to access all customer files uploaded to FileSender.&lt;/li&gt;
&lt;li&gt;  The security researcher then tried modifying the message in the JSON as shown above (decode, modify, re-encode and then send in HTTP request) to  &lt;code&gt;cfg:db_username&lt;/code&gt;  (without curly brackets), and the validation step in line 365 above didn’t replace this (as it doesn’t have a curly bracket), then as we have seen earlier a set of curly brackets was added in another place in the code, leading to the templating engine now replacing the config option with its value and exposing the database username as shown below:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvyjsw7u9ac60gf09e6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvyjsw7u9ac60gf09e6q.png" alt="DB username exposed" width="720" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  This allowed exposing the values of all the config values mentioned above including the database and S3 credentials using this unauthenticated route, which then could be used to expose or tamper with all customer files directly on S3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6decj6j9tgdr7a3bey3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6decj6j9tgdr7a3bey3.png" alt="All Files on S3 exposed" width="720" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;FileSender fixed this issue by adding validation which only allows a list of allowed exception message identifiers in the  &lt;code&gt;message&lt;/code&gt;  field such as the value  &lt;code&gt;transfer_presumed_expired&lt;/code&gt;  used in the original decoded string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Keep error messages generic&lt;/strong&gt;: This vulnerability is a strong reminder to keep error messages in your web application as generic as possible, as any extra information while helpful for troubleshooting, gives more context to any potential attacker.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Secure Design&lt;/strong&gt;: In this case, I would argue that using a templating engine to parse the error message is a design mistake. The developers tried to compensate for this design mistake through the validation applied in line 365 above, but using validation to compensate for design issues doesn’t always work as we have seen in this case. Your best bet to catch such design mistakes is integrating threat modeling in your SDLC process. For more about threat modeling you can check my series  &lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/threat-modeling-handbook-309a70ec273f" rel="noopener noreferrer"&gt;Threat Modeling Handbook&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Input validation&lt;/strong&gt;: Another important missing control here was input validation, the  &lt;code&gt;message&lt;/code&gt;  field here should be one of a list of expected values (exception identifiers such as  &lt;code&gt;transfer_presumed_expired&lt;/code&gt;), and if the backend validated that the passed  &lt;code&gt;message&lt;/code&gt;  was in fact one of these allowed values before applying any logic, this would have prevented the issue (this was also the actual fix FileSender ended up using for this vulnerability as mentioned above). Generally, input validation is one of the most powerful security controls that is usually easy to implement and is effective against a wide range of security attacks (such as SSTI in this case). You can find more details about input validation in this  &lt;a href="https://medium.com/appsec-untangled/how-to-make-input-validation-easy-for-your-devs-b31a4e8595cb" rel="noopener noreferrer"&gt;story&lt;/a&gt;  I’ve posted earlier.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Client-side encryption&lt;/strong&gt;: If you are hosting customer files, it is always a good idea to allow your customers to encrypt their files with their own keys (client-side), in addition to any server-side encryption you apply, this gives extra protection in case the files do get exposed through a vulnerability like this one. In this case, FileSender did provide a client-side encryption feature, and customers using this feature had that extra protection.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pentests and Bug Bounty&lt;/strong&gt;: It seems from the validation in line 365 above that the developers did know that such issue could exist but thought that the validation they had was sufficient, this is why it is important to schedule periodic pentests and/or allow bug bounty hunters to test your application to verify whether your assumptions are correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The most innocent-looking features of your application such as error messages, coupled with suboptimal design decisions and insufficient validation could lead to severe security issues. That is why developers and security engineers need to understand and discuss the implications of the design and implementation decisions as early as possible in the SDLC process and perform proper testing to confirm their assumptions are correct.&lt;/p&gt;

&lt;p&gt;Stay tuned for another story, and another set of Lessons Learned!&lt;/p&gt;

</description>
      <category>appsec</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>design</category>
    </item>
    <item>
      <title>Lessons Learned #3: Is your random UUID really random? (Account takeover with the sandwich 🥪 attack)</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Sun, 19 Jan 2025 22:17:14 +0000</pubDate>
      <link>https://dev.to/mohamed_aboelkheir/lessons-learned-3-is-your-random-uuid-really-random-account-takeover-with-the-sandwich-attack-4c1n</link>
      <guid>https://dev.to/mohamed_aboelkheir/lessons-learned-3-is-your-random-uuid-really-random-account-takeover-with-the-sandwich-attack-4c1n</guid>
      <description>&lt;p&gt;Welcome to the third story in the “&lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/lessons-learned-d64346d08a74" rel="noopener noreferrer"&gt;Lessons Learned&lt;/a&gt;” series where we discuss real-world vulnerabilities from the perspective of an application security engineer focusing on the underlying root causes and the measures we can take to prevent similar issues in our applications.&lt;/p&gt;

&lt;p&gt;In today’s story, we discuss a very interesting bug-bounty write-up showing a 0-click ATO (account takeover) using a clever technique called the Sandwich 🥪 attack, you can find the full write-up &lt;a href="https://www.landh.tech/blog/20230811-sandwich-attack/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, credit to &lt;a href="https://www.landh.tech/about" rel="noopener noreferrer"&gt;Lupin &amp;amp; Holmes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact of the vulnerability
&lt;/h2&gt;

&lt;p&gt;As this is a bug bounty write-up the application affected wasn’t disclosed, but the impact was a 0-click ATO which means one user of the application could take over another user’s account without the victim user having to do anything, the attacker only needs to know the username or email of the victim.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong?
&lt;/h2&gt;

&lt;p&gt;Like the vast majority of web applications, the affected application had a “Reset Password” functionality to help users who forgot their passwords, and like most applications, the functionality worked by following the below sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take the email of the user who forgot the password.&lt;/li&gt;
&lt;li&gt;Verify the email exists.&lt;/li&gt;
&lt;li&gt;Generate a random Id that corresponds to the user, and store it in the application database.&lt;/li&gt;
&lt;li&gt;Send an email to the user with a link to the reset password endpoint including the random Id in the query parameters. e.g. &lt;a href="https://password.application.com/token=ae0010a2-a6ed-11ef-b2c2-d26f418147d3" rel="noopener noreferrer"&gt;https://password.application.com/token=ae0010a2-a6ed-11ef-b2c2-d26f418147d3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;When the reset password endpoint gets an HTTP request it verifies the random Id exists in the database, and if it does it persists the new password for the corresponding user.
Note that the security of this feature depends on the fact that the reset password random Id is long enough and has enough entropy (randomness) and hence can’t be guessed by an attacker trying to reset the password of another user to take over their account.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  UUID version 1
&lt;/h2&gt;

&lt;p&gt;Well, turns out this is not entirely true. The security researcher found that the application used UUID &lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_1_and_6_(date-time_and_MAC_address)" rel="noopener noreferrer"&gt;version 1&lt;/a&gt; to generate the random Id used for the reset password links, and UUIDv1 relies mainly on the MAC address of the device and the timestamp of generation instead of being random. That is why if you try generating multiple uuids using UUIDv1 on your device part of the uuid will always be the same (depends on the MAC address), and the part that is different is the hexadecimal representation of the timestamp of the uuid generation.&lt;/p&gt;

&lt;p&gt;The uuids generated using UUIDv1 have the below structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First 3 parts are the hexadecimal representation of the timestamp of the uuid generation.&lt;/li&gt;
&lt;li&gt;Last 2 parts depend on the MAC address and system information, hence will always be the same if the uuid is generated on the same device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy4vsvh34we7kq3vw88w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy4vsvh34we7kq3vw88w.png" alt="UUIDv1 structure" width="640" height="460"&gt;&lt;/a&gt;&lt;br&gt;
Let’s give that a try in Python’s implementation of UUIDv1&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7gq34qkf8co2hrirglx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7gq34qkf8co2hrirglx.png" alt="UUIDv1 in Python" width="567" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see the first part of the uuid (in red) depends on the time stamp, that is why the last 2 parts are the same in all generated uuids as they were generated within a short period of time and have close timestamps, and the second part of the uuid (in yellow) is the same for all uuids as they were all generated on the same laptop. This isn’t very random, is it?&lt;/p&gt;

&lt;p&gt;You can also convert the first part of the UUID to a readable timestamp with a function like the one below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;uuid1&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;uuid1_to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid1_str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Parse the UUID string to a UUID object
&lt;/span&gt;    &lt;span class="n"&gt;uuid_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid1_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;uuid_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid_obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;
    &lt;span class="n"&gt;uuid_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid_time&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1e7&lt;/span&gt;

    &lt;span class="n"&gt;uuid_epoch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1582&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;unix_epoch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1970&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;epoch_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix_epoch&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uuid_epoch&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;unix_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid_seconds&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;epoch_offset&lt;/span&gt;
    &lt;span class="n"&gt;readable_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;readable_date&lt;/span&gt;

&lt;span class="c1"&gt;# Example usage
&lt;/span&gt;&lt;span class="n"&gt;uuid1_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;uuid1&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; 
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid1_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;datetime_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuid1_to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid1_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Timestamp in UUID:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which gives the below output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python test.py
155da2e8-a75f-11ef-b3ee-d266e835d4e1
Timestamp in UUID: 2024-11-20 11:47:09.396452

$ python test.py
aa604f0e-a763-11ef-9e7f-d266e835d4e1
Timestamp in UUID: 2024-11-20 12:19:57.381403
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Sandwich 🥪 Attack
&lt;/h2&gt;

&lt;p&gt;Now what remains is how the security researcher was able to exploit this un-random uuid issue, which was a very clever attack the worked in the below sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attacker requests a reset password link for their own account, they will get the link in their email, this is the first slice of bread 🍞.&lt;/li&gt;
&lt;li&gt;Very quickly, the attacker also requests a reset password link for the victim’s account, the attacker won’t get this link of course, this is what’s inside the sandwich 🧀.&lt;/li&gt;
&lt;li&gt;Also very quickly, the attacker requests another reset password link for their own account, and they will also get this link in their email, this is the second slice of bread 🍞.&lt;/li&gt;
&lt;li&gt;Now the attacker has 2 uuids for the 2 links generated for their own account, and there is another uuid for the victim account we don’t know yet, this is the one we need to find to takeover the victim account.&lt;/li&gt;
&lt;li&gt;As all of the uuids are generated using UUIDv1 the last part (in yellow in the example above) is always the same so we can get that from either of the 2 links the attacker already has.&lt;/li&gt;
&lt;li&gt;What remains is the first part which depends on the timestamp (in red in the example above) which we don’t know, but we know it is a timestamp between the 2 timestamps in the links the attacker has (the 2 slices of bread). Hence, the security researcher created a script that generated a list of all timestamps between the 2 timestamps in the links in the attacker link, and used that to generate uuids and links then used these links to brute force the application until the correct id was found.&lt;/li&gt;
&lt;li&gt;Once the correct id was found the attacker could use it to reset the password of the victim account and take over the account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7od14j8l6mwlz1xms1he.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7od14j8l6mwlz1xms1he.png" alt="The Sandwich 🥪 Attack" width="720" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;This issue can be fixed by switching to UUID &lt;a href="https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)" rel="noopener noreferrer"&gt;version 4&lt;/a&gt; which relies on random number generation, making it impossible to guess the generated uuids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;In this case the issue is more related to the implementation than to the design of the reset password functionality, and there are multiple things we can do that could help avoid this issue and similar issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt;: This attack needed brute forcing to work, and this could have been prevented by rate limiting. In this case, as the route is not authenticated you can limit the number of requests per client IP per second or per minute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always review crypto usage with security&lt;/strong&gt;: Poor choice of crypto algorithms could lead to different kinds of security issues, so it is always a good idea to review any usage of crypto with your security team. And yes, random number/id generation should be included in crypto operations. This is also a good topic to discuss during the threat modeling of your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use SAST and Linters&lt;/strong&gt;: This kind of implementation issue could be detected automatically using tools SAST and Linters as the vulnerable functions are known. For example, in this case I couldn’t find a SAST rule to detect the usage of UUIDv1, but I took advantage of Semgrep’s &lt;a href="https://semgrep.dev/docs/writing-rules/rule-ideas" rel="noopener noreferrer"&gt;Custom rules&lt;/a&gt; feature to add a rule to detect the usage of UUIDv1 in Python in the Semgrep Open source &lt;a href="https://semgrep.dev/r" rel="noopener noreferrer"&gt;Rule Registry&lt;/a&gt;. Here is the Pull Request I submitted to add the rule &lt;a href="https://github.com/semgrep/semgrep-rules/pull/3517" rel="noopener noreferrer"&gt;https://github.com/semgrep/semgrep-rules/pull/3517&lt;/a&gt;
Here’s an example of findings generated by the new rule I added:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ semgrep -c insecure-uuid-version.yaml .

┌──── ○○○ ────┐
│ Semgrep CLI │
└─────────────┘

Scanning 180 files (only git-tracked) with 1 Code rule:

  CODE RULES
  Scanning 91 files.

  SUPPLY CHAIN RULES

  No rules to run.


  PROGRESS

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00                                                                                                                        


┌─────────────────┐
│ 3 Code Findings │
└─────────────────┘

    insecure-uuid-version.py
    ❯❱ insecure-uuid-version
          Using UUID version 1 for UUID generation can lead to predictable UUIDs based on system information
          (e.g., MAC address, timestamp). This may lead to security risks such as the sandwich attack. 
          Consider using `uuid.uuid4()` instead for better randomness and security.                                                                

           ▶▶┆ Autofix ▶ uuid.uuid4()
            4┆ uuid = uuid.uuid1()
            ⋮┆----------------------------------------
           ▶▶┆ Autofix ▶ uuid4()
            9┆ uuid = uuid1()
            ⋮┆----------------------------------------
           ▶▶┆ Autofix ▶ uuid4()
           14┆ uuid = uuid1()                

┌──────────────┐
│ Scan Summary │
└──────────────┘
Some files were skipped or only partially analyzed.
  Scan was limited to files tracked by git.

Ran 1 rule on 91 files: 3 findings.

⏫ A new version of Semgrep is available. See &amp;lt;https://semgrep.dev/docs/upgrading&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Side Challenges
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Use the above Semgrep custom rule as reference and submit a new Semgrep rule that detects the usage of UUIDv1 in another language such as Java or Javascript. Share with me the Pull Request the in comments if you do.&lt;/p&gt;

&lt;p&gt;💡 Create a rule in any Linter you use (e.g. ESLint) that detects the usage of UUIDv1. Share the rule with me in the comments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The fact that the usage of the &lt;code&gt;uuid1()&lt;/code&gt; function instead of &lt;code&gt;uuid4()&lt;/code&gt; could lead to an account takeover vulnerability just shows that for many security issues, the devil truly lies in the details. This is why your approach to application security should be multi-layered to cover both the design and the implementation. It is also useful to use automation to convert any lessons learned for past issues to rules and checks in your tools (like the Semgrep rule above). Hope you found this useful, have a great day ahead!&lt;/p&gt;

</description>
      <category>appsec</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>design</category>
    </item>
    <item>
      <title>Lessons Learned #2: Your new feature could introduce a security vulnerability to your old feature (Clickhouse CVE-2024-22412)</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Wed, 25 Sep 2024 17:28:54 +0000</pubDate>
      <link>https://dev.to/mohamed_aboelkheir/lessons-learned-2-your-new-feature-could-introduce-a-security-vulnerability-to-your-old-feature-clickhouse-cve-2024-22412-53o8</link>
      <guid>https://dev.to/mohamed_aboelkheir/lessons-learned-2-your-new-feature-could-introduce-a-security-vulnerability-to-your-old-feature-clickhouse-cve-2024-22412-53o8</guid>
      <description>&lt;p&gt;This is the second story in the “&lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/lessons-learned-d64346d08a74" rel="noopener noreferrer"&gt;Lessons Learned&lt;/a&gt;” series where we discuss real-world vulnerabilities from the eyes of an application security engineer, with a focus on the underlying root causes and the measures we can take to prevent similar issues in our applications.&lt;/p&gt;

&lt;p&gt;In today’s story, we will discuss &lt;a href="https://github.com/ClickHouse/ClickHouse/security/advisories/GHSA-45h5-f7g3-gr8r" rel="noopener noreferrer"&gt;CVE-2024-22412&lt;/a&gt; which affected &lt;a href="https://clickhouse.com/" rel="noopener noreferrer"&gt;ClickHouse&lt;/a&gt; a popular open-source column-oriented database management system typically used for online analytical processing (OLAP) in real-time. You can find the full write-up of the vulnerability &lt;a href="https://blog.runreveal.com/cve-2024-22412-behind-the-bug-a-classic-caching-problem-in-the-clickhouse-query-cache/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact of the vulnerability
&lt;/h2&gt;

&lt;p&gt;This vulnerability could lead to authorization bypass under specific conditions, potentially leading to the exposure of sensitive data stored in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong?
&lt;/h2&gt;

&lt;p&gt;ClickHouse had previously introduced a feature that allowed role-based access control to any table based on the value of a column. For example you can create 2 roles, one that is only allowed to access to rows where &lt;code&gt;user_id = 1&lt;/code&gt; and another role is only allowed access to rows where &lt;code&gt;user_id = 2&lt;/code&gt; with the statements below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;user_role_1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;user_role_1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;user_policy_1&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;user_role_1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;user_role_2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;user_role_2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;user_policy_2&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;user_role_2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;user_role_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_role_2&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when selecting from the table using these 2 roles the results will vary based on the role as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgvj9sfgeu1xxz1f6jdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgvj9sfgeu1xxz1f6jdx.png" alt="Role-based access control"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This “Role-based access control” feature in itself was working fine until a new feature was introduced which is a “Query cache”. The goal of the new feature is to enhance performance by caching the results of queries, and returning the results from the cache if the same query is run.&lt;/p&gt;

&lt;p&gt;Now as you may have already guessed, the issue here was related to how these 2 features played together. The “Query cache” didn’t add the user role in the identifier of the query, making the same query run by 2 different roles look the same for the query cache, and subsequently returning the results of &lt;code&gt;user_role_1&lt;/code&gt; if they are already cached when &lt;code&gt;user_role_2&lt;/code&gt; is used, allowing access to rows the role shouldn’t be authorized to access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flha42l74prj8kkjfr21o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flha42l74prj8kkjfr21o.png" alt="Query cache introduced authorization bypass, user_role_2 can see the cached results of user_role_1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;ClickHouse fixed this by adding a patch incorporating current users and roles into the cache key, making the same query with 2 different roles have different cache keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Similar to the last &lt;a href="https://medium.com/appsec-untangled/lessons-learned-1-one-line-of-code-can-make-your-application-vulnerable-pre-auth-rce-in-metabase-a8579ca0102d" rel="noopener noreferrer"&gt;story&lt;/a&gt; of this series, this is a business logic issue specific to ClickHouse, so no security scanning tool (SAST, DAST, IAST, .. etc) could have detected this issue. For this kind of issue, your best lines of defense are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Threat modeling&lt;/strong&gt;: Spending time during the design phase of any project to decide what could go wrong would be a good place to discuss business logic issues and what can be done to avoid them (the mitigations). For complex issues involving multiple features like this one, Threat modeling is the activity that is most likely to catch such issues before being pushed to production. For more about threat modeling you can check my series &lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/threat-modeling-handbook-309a70ec273f" rel="noopener noreferrer"&gt;Threat Modeling Handbook&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security tests&lt;/strong&gt;: Covering the security properties of your features (e.g. the threat model mitigations) with unit or integration tests could also have helped with detecting new security issues being introduced after a feature is launched. That being said, note that issues related to caching are sometimes missed by unit and integration tests as they need a specific sequence of events to be reproducible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pentests and Bug Bounty&lt;/strong&gt;: If you miss such issues in your threat model and security tests, then having regular pentests and/or a bug bounty program can act as your safety net. In this case, the issue was reported to ClickHouse’s Bug Bounty program, which put them in a much better place that if an actual attacker discovered the issue and tried to exploit it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Software is complex, and while 2 features could be working well separately, they could introduce a security vulnerability when combined. Hence, it is always good to consciously consider the security implications of any new feature during the design phase (threat modeling) and to cover the security properties with tests to ensure they don’t get broken by future changes.&lt;/p&gt;

</description>
      <category>appsec</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>design</category>
    </item>
    <item>
      <title>Lessons Learned #1: One line of code can make your application vulnerable (Pre-Auth RCE in Metabase CVE-2023–38646)</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Tue, 03 Sep 2024 21:17:11 +0000</pubDate>
      <link>https://dev.to/mohamed_aboelkheir/lessons-learned-1-one-line-of-code-can-make-your-application-vulnerable-pre-auth-rce-in-metabase-cve-2023-38646-17ic</link>
      <guid>https://dev.to/mohamed_aboelkheir/lessons-learned-1-one-line-of-code-can-make-your-application-vulnerable-pre-auth-rce-in-metabase-cve-2023-38646-17ic</guid>
      <description>&lt;p&gt;Welcome all to this new series “Lessons Learned”. In this series, I plan to share some real-world vulnerabilities from the eyes of an application security engineer. There are many resources where you can find vulnerability write-ups which mostly focus on the exploitation techniques used to discover and exploit the vulnerability. This is usually pretty cool but is more relevant to security researchers/pentesters/bug bounty hunters.&lt;/p&gt;

&lt;p&gt;However, In this series, I will take a different angle that is more relevant to application security engineers and developers, so I will focus instead on the underlying root causes and the measures we can take to prevent similar issues in our applications.&lt;/p&gt;

&lt;p&gt;Of course, I will leave links for the vulnerabilities write-up if you are also interested in the exploitation techniques.&lt;/p&gt;

&lt;p&gt;So Let’s start with the first vulnerability!&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-Auth RCE in Metabase (CVE-2023–38646)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/metabase/metabase" rel="noopener noreferrer"&gt;Metabase&lt;/a&gt; is a popular open-source business intelligence tool. A vulnerability was discovered around July 2023 and assigned &lt;a href="https://www.metabase.com/blog/security-advisory" rel="noopener noreferrer"&gt;CVE-2023–38646&lt;/a&gt; (you can find the full write-up &lt;a href="https://www.assetnote.io/resources/research/chaining-our-way-to-pre-auth-rce-in-metabase-cve-2023-38646" rel="noopener noreferrer"&gt;here&lt;/a&gt;) which had a devastating impact of pre-auth RCE (Remote code execution) which means an unauthenticated user with network access to any instance of the web application could run code on the servers the application is running on. So Let’s discover what went wrong!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6kncqri2t0urqfyf1rg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6kncqri2t0urqfyf1rg.png" alt="Metabase" width="777" height="556"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metabase&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue #1: Authentication Bypass
&lt;/h2&gt;

&lt;p&gt;Like many applications, Metabase uses a setup token during the initialization of the application. Once the application is initialized, the setup token should no longer be useable, and instead, the user should use the credentials created during the initialization process.&lt;/p&gt;

&lt;p&gt;This was working as expected until one day a developer pushed a &lt;a href="https://github.com/metabase/metabase/commit/0526d88f997d0f26304cdbb6313996df463ad13f#diff-44990eafd7da3ac7942a9f232b56ec045c558fdc3c414a2439e42b5668eced32L140-L141" rel="noopener noreferrer"&gt;PR&lt;/a&gt; (pull request), and for some reason removed the line of code clearing the setup token after init is done, this wasn’t caught in the code review, and as a result for this version of Metabase and subsequent ones, the setup token was available after being in production for anyone that has network access.&lt;/p&gt;

&lt;p&gt;Any attacker that can find the setup token, can use it to bypass authentication and login to the web application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h0ghd30qcftkye6abeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h0ghd30qcftkye6abeq.png" alt="One line of code made the setup token available after the initialization" width="800" height="203"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;One line of code made the setup token available after the initialization&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue #2: SQL Injection
&lt;/h2&gt;

&lt;p&gt;As Metabase is a business intelligence application, it has to connect to multiple database, one of which is the &lt;a href="https://www.h2database.com/html/main.html" rel="noopener noreferrer"&gt;H2&lt;/a&gt; database. The security researchers in this case found a 0-day vulnerability (undiscovered vulnerability) affecting the H2 database driver allowing SQL injection.&lt;/p&gt;

&lt;p&gt;You can see the SQL injection payload in the screenshot below, and you can find more details about how it works in the &lt;a href="https://www.assetnote.io/resources/research/chaining-our-way-to-pre-auth-rce-in-metabase-cve-2023-38646" rel="noopener noreferrer"&gt;write-up&lt;/a&gt; itself. However, the thing to note here is that the reason this SQL injection was possible, is the fact that the application was taking the connection string itself as input which allowed the security researchers a lot of room to experiment and find ways to exploit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvm92pn6xa9w5a20l9le.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvm92pn6xa9w5a20l9le.png" alt="SQL injection payload" width="709" height="638"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;SQL injection payload&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authentication bypass&lt;/strong&gt;: The setup token issue is a business logic issue, very specific to Metabase. This means that no security scanning tool (SAST, DAST, IAST, .. etc) could have detected this issue. For this class of issues, your best bet is always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Threat modeling&lt;/strong&gt;: Spending time during the design phase of any project to decide what could go wrong would be a good place to discuss business logic issues and what can be done to avoid them (the mitigations). For more about threat modeling you can check my series &lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/threat-modeling-handbook-309a70ec273f" rel="noopener noreferrer"&gt;Threat Modeling Handbook&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security tests&lt;/strong&gt;: It is equally important to make sure the mitigations discussed during threat modeling, and any security controls are being covered by tests. This could be a unit test, an integration test, or a security tool scan depending on the situation. For example, in this case, an integration test verifying the setup token was no longer available after initialization would have detected the issue before being pushed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SQL injection&lt;/strong&gt;: In this case, the vulnerability was not in the code itself, but in the library used for H2 database connection. As this was a 0-day, SCA tools (e.g. Dependabot) couldn’t have helped. However, two things could have helped here:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not taking complex input&lt;/strong&gt; such as the connection string: The exploit would have been much less likely to work if we only took the host, port, and other details needed for the connection and used them to build the connection string within the code, making it harder to provide the payload needed for SQL injection to work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Input validation&lt;/strong&gt;: besides not taking complex, input it is always important to perform input validation that doesn’t allow unneeded special characters, this could be in the form of a pattern constraint (regular expression), a list of allowed values, or a list of allowed characters. This also makes it nearly impossible to provide the payload needed for SQL injection to work. You can find more about input validation in my previous story &lt;a href="https://dev.to/owasp/how-to-make-input-validation-easy-for-your-devs-2o6b"&gt;How to make “Input validation” easy for your devs&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Always remember that even one line of code could make your application vulnerable, and while code reviews work, you can’t fully rely on them. You should also make sure that any security control is covered by a test that is running during build or periodically to make sure new code changes don’t break the control.&lt;/p&gt;

&lt;p&gt;Stay tuned for the next episode of “Lessons Learned”!&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>design</category>
      <category>appsec</category>
    </item>
    <item>
      <title>How to make “Input validation” easy for your devs</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Tue, 23 Jul 2024 14:34:46 +0000</pubDate>
      <link>https://dev.to/owasp/how-to-make-input-validation-easy-for-your-devs-2o6b</link>
      <guid>https://dev.to/owasp/how-to-make-input-validation-easy-for-your-devs-2o6b</guid>
      <description>&lt;h2&gt;
  
  
  What is Input validation
&lt;/h2&gt;

&lt;p&gt;Input validation is one of the basic security controls that help protect against a wide range of web application attacks (e.g. SQL injection, Command injection, Directory traversal, .. etc). The goal of input validation is to discard any input that doesn’t look valid before it reaches a dangerous sink (e.g. an SQL statement).&lt;/p&gt;

&lt;p&gt;What qualifies as input depends on the type of the application, e.g. for a typical REST API accepting HTTP requests, input parameters could come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request headers&lt;/li&gt;
&lt;li&gt;Cookies&lt;/li&gt;
&lt;li&gt;GET parameters&lt;/li&gt;
&lt;li&gt;Route parameters, e.g. for the route &lt;code&gt;/api/resource/:resourceId&lt;/code&gt; the resource id is in the path/route e.g. for &lt;code&gt;/api/resource/12345678&lt;/code&gt; the resourceId is 12345678&lt;/li&gt;
&lt;li&gt;Request body, e.g. fields in a JSON object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each input parameter validation can take many forms but the most common ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List of allowed values. e.g. for a &lt;code&gt;type&lt;/code&gt; input parameter could only be one of the valid types (e.g. email, chat, or SMS).&lt;/li&gt;
&lt;li&gt;A pattern. e.g. for a &lt;code&gt;resource_id&lt;/code&gt; input parameter should be in the form of a UUID.&lt;/li&gt;
&lt;li&gt;Maximum length with a list of allowed characters. e.g. for a &lt;code&gt;description&lt;/code&gt; input parameter&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Input validation is needed
&lt;/h2&gt;

&lt;p&gt;This is effective because most attacks need some kind of weird-looking payload that includes special characters being passed as input (the source) and reaching a dangerous function (the sink). For example, an SQL injection payload may look like &lt;code&gt;value'; DROP TABLE users; --&lt;/code&gt;. However, when input validation is applied these kinds of payloads are usually discarded before reaching the dangerous sink.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5nwgxzekcq0gtct6n5h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5nwgxzekcq0gtct6n5h.png" alt="Input validation" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; While input validation is a useful security control, it doesn’t replace the need to replace dangerous sinks with safe ones. For e.g. for SQL injection you should always use &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html#defense-option-1-prepared-statements-with-parameterized-queries" rel="noopener noreferrer"&gt;parameterized queries&lt;/a&gt; to avoid SQL injection. Input validation should be used as a security-in-depth control just in case a dangerous sink is missed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paved road for Input validation
&lt;/h2&gt;

&lt;p&gt;Despite the value and the relatively low complexity of input validation, many developers skip implementing it for their applications. This is because we usually follow the least friction path to our goal, and input validation is not generally necessary for the application to work as expected, hence developers ignore it or de-prioritize it to focus on the actual logic of the application.&lt;/p&gt;

&lt;p&gt;This is why it is important for us application security engineers not to only focus on the importance of input validation, but also on how to make it easy for developers to implement it. Finding a way that makes it straightforward, easy to implement, and easy to audit a security control significantly reduces friction and increases adoption. In other words, we should find a “&lt;a href="https://netflixtechblog.com/the-show-must-go-on-securing-netflix-studios-at-scale-19b801c86479" rel="noopener noreferrer"&gt;Paved road&lt;/a&gt;” for our developers to implement input validation.&lt;/p&gt;

&lt;p&gt;For input validation, one way to do that is to bundle the input validation logic, and the behavior when input validation fails (e.g. return 400 status code with a custom error message) into some sort of middleware, and then use this middleware for our routes. This way developers only need to add this middleware to the routes and specify the expected parameters for each route with the corresponding type of validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Express as an example
&lt;/h2&gt;

&lt;p&gt;As an example, let’s assume we are using Node.JS and &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt; framework for our backend. We can implement this middleware which uses the &lt;a href="https://joi.dev/" rel="noopener noreferrer"&gt;Joi&lt;/a&gt; validation library under the hood. The middleware can look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;joi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;params&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;abortEarly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validationResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, all what developers need to do is to specify a schema that defines the inputs coming from the request body, query parameters, path parameters, headers and cookies, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// only emails are accepted&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// minimum length is 6 characters&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^[a-zA-Z0-9]{3,30}$&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// a regular expression&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// list of allowed values&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Allow other headers&lt;/span&gt;
  &lt;span class="na"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookie1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// only uuids are accepted&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookie2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="c1"&gt;// list of allowed values&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scheme specifies the body parameters, headers, and cookies expected and their allowed values/patterns. Any other parameters or any invalid value will automatically return a 400 response code.&lt;/p&gt;

&lt;p&gt;Then the developers just need to use the middleware with the schema when defining the route, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createUserSchema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// Generate a unique ID for the user (for demonstration purposes)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Create a new user object from the request body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="c1"&gt;// Add the new user to the users object using the generated ID&lt;/span&gt;
  &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Respond with the newly created user object&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Auditing the use of the middleware
&lt;/h2&gt;

&lt;p&gt;Also, we can use a tool like &lt;a href="https://semgrep.dev/" rel="noopener noreferrer"&gt;Semgrep&lt;/a&gt; to audit the use of the middleware through all routes using a custom rule, to show a simple example we can use a rule like the below (Note this is just example which covers one way of defining routes in Express, for production the rule needs to be extended to include all other ways of defining routes).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;express-route-without-validate-middleware&lt;/span&gt;
    &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;router.route($ROUTE).$METHOD(...)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern-not-inside&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;router.route($ROUTE).$METHOD(validate(...), $HANDLER)&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Route&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;declared&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;without&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;validate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;middleware"&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WARNING&lt;/span&gt;
    &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;javascript&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;typescript&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;security&lt;/span&gt;
      &lt;span class="na"&gt;technology&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;express&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Add&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;validate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;middleware&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;route"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When forgetting to add the middleware, this rule would generate a finding for the developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The above example is just one way to create a paved road to make input validation easier for your developers, obviously based on what languages and frameworks your developers are using you may need to adapt your approach. My recommendation is to have a discussion with your dev teams and security champions to decide on the best way to implement a solution that works for your organization, and make your goal for the agreed solution to be easy to use, and easy to audit as shown in the above example.&lt;/p&gt;

</description>
      <category>security</category>
      <category>express</category>
      <category>cybersecurity</category>
      <category>node</category>
    </item>
  </channel>
</rss>
