<?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>What AppSec Engineers Actually Do (and Why It Matters)</title>
      <dc:creator>Mohamed AboElKheir</dc:creator>
      <pubDate>Tue, 28 Apr 2026 19:21:32 +0000</pubDate>
      <link>https://dev.to/mohamed_aboelkheir/what-appsec-engineers-actually-do-and-why-it-matters-58fl</link>
      <guid>https://dev.to/mohamed_aboelkheir/what-appsec-engineers-actually-do-and-why-it-matters-58fl</guid>
      <description>&lt;p&gt;Imagine a team building a simple feature: an endpoint to let users download their invoices. The implementation is straightforward: check the user is authenticated, fetch the file, and return it. It passes code review, tests are green, and it ships. A few weeks later, someone realizes you can tweak a parameter and download another user’s invoice, and the team now has to rush to fix this security issue.&lt;/p&gt;

&lt;p&gt;No software engineer sets out to write insecure code. However, stories like this one keep happening, and if you think about it, for the team building the feature, nothing “looked” insecure during development, and that is because no one had clearly defined what secure meant for that feature in the first place.&lt;/p&gt;

&lt;p&gt;This is the real gap AppSec engineers fill. Their job isn’t just to run tools and share the findings with engineering, but to define what “Secure” means in the context of what is being built by helping teams ask the right questions early: Who should have access to this data? What could go wrong? How might someone abuse this flow? By identifying relevant threats and defining the right mitigations upfront, AppSec turns security from guesswork into something concrete, something teams can actually build against, verify, and test.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Value AppSec Engineers Add
&lt;/h2&gt;

&lt;p&gt;At its core, everything AppSec engineers do revolves around two goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define “Security”:&lt;/strong&gt; The meaning of the word “Secure” is incomplete until we specify what we need to be secure from (the threats), and what we can do about them (the mitigations). This applies at every level, from full systems to individual features to single pull requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce “Security”:&lt;/strong&gt; Based on the identified threats and mitigations, AppSec ensures they are consistently applied. This means using the right combination of processes and tools to prevent new issues, detect existing vulnerabilities, and help teams fix them effectively.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  AppSec is a Force Multiplier, not a Gatekeeper
&lt;/h2&gt;

&lt;p&gt;AppSec works closely with engineering, platform, and other teams to apply the processes and tools needed to achieve these goals across the SDLC. But doing this well requires balance. If AppSec introduces friction, slows teams down, or becomes a gatekeeper, it quickly loses trust and becomes counterproductive. Effective AppSec enables teams to move fast while staying secure, not the other way around.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AppSec Gets This Done
&lt;/h2&gt;

&lt;p&gt;To achieve these goals while maintaining the right balance, AppSec applies a combination of processes and tools across different stages of the SDLC. Here are the most important ones:&lt;/p&gt;

&lt;h3&gt;
  
  
  Threat Modeling
&lt;/h3&gt;

&lt;p&gt;A Threat Model focuses on answering 4 main questions:&lt;/p&gt;

&lt;p&gt;What are we working on? → &lt;strong&gt;Scope&lt;/strong&gt;&lt;br&gt;
What could go wrong? → &lt;strong&gt;Threats&lt;/strong&gt;&lt;br&gt;
What could we do about it? → &lt;strong&gt;Mitigations&lt;/strong&gt;&lt;br&gt;
Did we do a good job? → &lt;strong&gt;Verification and Testing&lt;/strong&gt;&lt;br&gt;
And the answer to these questions is how we achieve the “Define Security” goal for what is being built. Hence, adding “Threat Modeling” to the Design stage of the SDLC for new projects is an essential component of any AppSec program, and it also drives many subsequent processes and tool choice.&lt;/p&gt;

&lt;p&gt;For more details about threat modeling, you can check the &lt;a href="https://medium.com/@mohamed.osama.aboelkheir/list/threat-modeling-handbook-309a70ec273f" rel="noopener noreferrer"&gt;Threat Modeling Handbook&lt;/a&gt; series.&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%2Fgi5xsskzs7iue6iavvc6.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%2Fgi5xsskzs7iue6iavvc6.png" alt="Threat modeling starts at the Design phase" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Review Process
&lt;/h3&gt;

&lt;p&gt;Threat Modeling is ideally the first phase of a “Security Review Process”. One of the outputs of the threat model should be a testing plan to verify that the threats identified are properly mitigated, which should be performed during the “Testing” phase.&lt;/p&gt;

&lt;p&gt;Mitigations can be verified in different ways, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code Review&lt;/li&gt;
&lt;li&gt;Security Testing (e.g. using Burp Suite)&lt;/li&gt;
&lt;li&gt;Security tools (e.g. using SAST)
Also, some mitigations should be covered by continuous tests (e.g. unit or integration tests) to make sure they aren’t broken by future code changes, which should also be covered in the testing plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, AppSec and engineering can’t review every project due to time constraints. Instead, we should use a set of criteria to prioritize work based on risk and complexity. Tuning these criteria is critical; AppSec should focus on the highest-impact areas without taking on more reviews than it can handle and becoming a bottleneck.&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%2Fkp6czmy0gmb96nmnk9a3.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%2Fkp6czmy0gmb96nmnk9a3.png" alt="Security Review Process" width="720" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Review
&lt;/h3&gt;

&lt;p&gt;Besides the Security Review process for new projects, we also need to review individual pull requests that meet specific criteria (e.g. pull requests adding or modifying new endpoints) to make sure they don’t introduce new security issues. This usually follows the same steps as the Security Review process (threat modeling followed by security testing), but on a smaller scale. As with project-level reviews, tuning these criteria is important to focus on high-impact changes without creating unnecessary friction.&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%2Fzf70eputivhj0l7ex2v7.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%2Fzf70eputivhj0l7ex2v7.png" alt="High Risk Pull Request should go through an AppSec Code Review" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Tools
&lt;/h3&gt;

&lt;p&gt;The use of security tools to automate verifying threats are mitigated is important for efficiency, and for coverage (e.g. code not covered by the review process is covered by tools). However, there are 2 common pitfalls AppSec should avoid with security tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Relying on security tools to “Define Security” and prioritize work. Security tools usually can’t consider the context and business logic of the application, hence they can generate a lot of false positives or findings of low priority. We should always start with “Threat modeling” and then select and configure the security tools that cover the mitigations we identify, to make sure we focus on the high-impact findings in the context of the application.&lt;/li&gt;
&lt;li&gt;Sharing untriaged findings with engineering. Sending too many false positives, incorrect severities, or non-exploitable security issues to engineering is a great way to lose their trust. AppSec should always perform triage of any finding before sending it to engineering to ensure the issue is reproducible, the severity is accurate, and suggest a fix whenever possible.
With that in mind, different tools can be used to detect security issues in the multiple layers of the application. Also, we need to mix between incremental scans (e.g. PR scans) and full scans (e.g. scheduled daily or weekly) for best results. Here are some of these tools:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SAST:&lt;/strong&gt; Static code scanning helps detect specific types of vulnerabilities affecting the application code, mainly the ones related to using a dangerous function/sink (e.g. use of a function that can lead to SQL injection).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unit and Integration tests:&lt;/strong&gt; Engineering teams already use unit tests and integration tests, and they are a great fit for testing the mitigations related to the application business logic (e.g. authentication and authorization), which are typically not covered by SAST.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SCA:&lt;/strong&gt; Applications need open source packages (e.g. npm or pip packages) to run, and these packages could be affected by some vulnerabilities. Hence, we should be scanning these packages for known vulnerabilities. It is recommended to use tools that support &lt;a href="https://medium.com/appsec-untangled/how-reachability-analysis-can-help-with-open-source-vulnerabilities-mess-coana-as-an-example-54c55ba74cde" rel="noopener noreferrer"&gt;Reachability analysis&lt;/a&gt; to make sure we are focusing on the vulnerabilities that are reachable and potentially exploitable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container vulnerability scanning:&lt;/strong&gt; Similarly, if your application is running on a container, this container could have some packages with known vulnerabilities, so we should be scanning the container images used and prioritizing fixing the ones that are potentially exploitable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud config scanning:&lt;/strong&gt; If the application is running on a cloud service, scanning the account for misconfigurations (e.g. an S3 bucket with customer data set to allow public access).
For more details about security tools, you can check &lt;a href="https://devopsroadmap.io/growth/devsecops/" rel="noopener noreferrer"&gt;https://devopsroadmap.io/growth/devsecops/&lt;/a&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%2Fczahblxy8ggth27htesp.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%2Fczahblxy8ggth27htesp.png" alt="Different tools cover different layers of the applicaiton" width="800" height="1476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pentests and Bug Bounty
&lt;/h3&gt;

&lt;p&gt;Besides the internal pentesting AppSec performs as part of the security review process, it is recommended to schedule periodic pentests performed by vendors, as well as start a bug bounty program when the AppSec program is mature enough to handle it. Both external pentests and bug bounty act as a safety net to help detect issues missed by our tools and processes, and as a feedback loop to use these misses to improve our tools and processes (e.g. add more tests to our scanning tools, or add more threats to our threat modeling library).&lt;/p&gt;

&lt;h3&gt;
  
  
  People
&lt;/h3&gt;

&lt;p&gt;AppSec should also be investing in promoting a health security culture within the engineering team, which can be done through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providing periodic training sessions focused on AppSec.&lt;/li&gt;
&lt;li&gt;Providing resources, e.g. an AppSec wiki showing the most important threats and their mitigations in our applications.&lt;/li&gt;
&lt;li&gt;Building relationships and communication channels with stakeholders.&lt;/li&gt;
&lt;li&gt;AppSec champions program, where software engineers interested in security can learn more, and act as advocates for security within their team.&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%2F98zu2peutxke7d5a9g0u.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%2F98zu2peutxke7d5a9g0u.png" alt="Example of an AppSec program’s processes and tools" width="800" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AppSec Skills
&lt;/h2&gt;

&lt;p&gt;Being able to perform the processes and use the tools to run a successful AppSec program requires a set of skills that spans multiple domains, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A strong understanding of common security issues and vulnerabilities (e.g., OWASP Top 10).&lt;/li&gt;
&lt;li&gt;Solid knowledge of system architecture and application design.&lt;/li&gt;
&lt;li&gt;The ability to apply that knowledge during threat modeling by mapping relevant risks to what is being built.&lt;/li&gt;
&lt;li&gt;Comfort reading and writing code, enabling effective code reviews, validating mitigations, and contributing fixes that make systems secure by default where possible.&lt;/li&gt;
&lt;li&gt;Hands-on experience with pentesting and security testing.&lt;/li&gt;
&lt;li&gt;A good understanding of the underlying infrastructure, including cloud platforms and CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;When done well, AppSec doesn’t slow teams down. It removes uncertainty, reduces rework, and helps engineers build with confidence. That’s what makes it a force multiplier.&lt;/p&gt;

&lt;p&gt;If you’re an engineer, this means security shouldn’t feel like a last-minute hurdle; it should be something you can design and build for from the start. If you’re building an AppSec program, success isn’t measured by the number of findings, but by how effectively you help teams prevent them in the first place. And if you’re looking to become an AppSec engineer, your impact will come from how well you can bridge the gap between risk and real-world systems.&lt;/p&gt;

&lt;p&gt;That’s what AppSec engineers actually do, and why it matters.&lt;/p&gt;

</description>
      <category>appsec</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>design</category>
    </item>
    <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>
