CINXE.COM
Escape's Business Logic Security Testing Algorithm
<!DOCTYPE html> <html lang="en"> <head> <title>Escape's Business Logic Security Testing Algorithm</title> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="HandheldFriendly" content="True" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <style> :root { --button-bg-color: #ffffff; --button-text-color: var(--color-darkgrey); } </style> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,700&family=Inter:wght@400;500;600;700&display=swap"> <link rel="stylesheet" type="text/css" href="/blog/assets/built/screen.css?v=bdd0505571" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/themes/prism-tomorrow.min.css" integrity="sha512-vswe+cgvic/XBoF1OcM/TeJ2FW0OofqAVdCZiEYkd6dwGXthvkSFWOoGGJgS2CW70VK5dQM5Oh+7ne47s74VTg==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.css"> <meta name="description" content="In this article, we'll show how we created Escape's proprietary business logic security testing algorithm and what makes it innovative."> <link rel="icon" href="https://escape.tech/blog/content/images/size/w256h256/2021/09/ESCAPE-LOGO-28-07-2021-08_1000.png" type="image/png"> <link rel="canonical" href="https://escape.tech/blog/escape-proprietary-algorithm/"> <meta name="referrer" content="no-referrer-when-downgrade"> <link rel="amphtml" href="https://escape.tech/blog/escape-proprietary-algorithm/amp/"> <meta property="og:site_name" content="Escape - The API Security Blog"> <meta property="og:type" content="article"> <meta property="og:title" content="Escape's Business Logic Security Testing Algorithm"> <meta property="og:description" content="In this article, we'll show how we created Escape's proprietary business logic security testing algorithm and what makes it innovative."> <meta property="og:url" content="https://escape.tech/blog/escape-proprietary-algorithm/"> <meta property="og:image" content="https://escape.tech/blog/content/images/2024/04/business-logic-algorithm-innovative.png"> <meta property="article:published_time" content="2024-04-17T16:57:18.000Z"> <meta property="article:modified_time" content="2025-02-13T06:32:55.000Z"> <meta property="article:tag" content="API Security"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="Escape's Business Logic Security Testing Algorithm"> <meta name="twitter:description" content="In this article, we'll show how we created Escape's proprietary business logic security testing algorithm."> <meta name="twitter:url" content="https://escape.tech/blog/escape-proprietary-algorithm/"> <meta name="twitter:image" content="https://escape.tech/blog/content/images/2024/04/business-logic-algorithm-innovative-1.png"> <meta name="twitter:label1" content="Written by"> <meta name="twitter:data1" content="Antoine Carossio"> <meta name="twitter:label2" content="Filed under"> <meta name="twitter:data2" content="API Security"> <meta name="twitter:site" content="@EscapeTechHQ"> <meta name="twitter:creator" content="@iCarossio"> <meta property="og:image:width" content="1050"> <meta property="og:image:height" content="586"> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "publisher": { "@type": "Organization", "name": "Escape - The API Security Blog", "url": "https://escape.tech/blog/", "logo": { "@type": "ImageObject", "url": "https://escape.tech/blog/content/images/2022/05/escape-logo.0e6d59f.svg", "width": 141, "height": 36 } }, "author": { "@type": "Person", "name": "Antoine Carossio", "image": { "@type": "ImageObject", "url": "https://escape.tech/blog/content/images/2024/08/Antoine-Escape.jpg", "width": 1200, "height": 1183 }, "url": "https://escape.tech/blog/author/antoine/", "sameAs": [ "https://www.linkedin.com/in/acarossio", "https://twitter.com/iCarossio" ] }, "headline": "Escape's Business Logic Security Testing Algorithm", "url": "https://escape.tech/blog/escape-proprietary-algorithm/", "datePublished": "2024-04-17T16:57:18.000Z", "dateModified": "2025-02-13T06:32:55.000Z", "image": { "@type": "ImageObject", "url": "https://escape.tech/blog/content/images/2024/04/business-logic-algorithm-innovative.png", "width": 1050, "height": 586 }, "keywords": "API Security", "description": "Testing APIs for Business Logic vulnerabilities is hard. Actually, this is a mission that old-school DAST solutions like ZAP (formerly OWASP ZAP) cannot handle.\n\nI'm Antoine Carossio, passionate about Computer Science for more than 15 years now and cofounder & CTO of Escape. With my team, we've spent 3 years building a proprietary algorithm capable of finding Business Logic vulnerabilities in all API types, especially in the most recent ones like REST & GraphQL.\n\nHowever, simply stating its exis", "mainEntityOfPage": "https://escape.tech/blog/escape-proprietary-algorithm/" } </script> <meta name="generator" content="Ghost 5.109"> <link rel="alternate" type="application/rss+xml" title="Escape - The API Security Blog" href="https://escape.tech/blog/rss/"> <script defer src="https://cdn.jsdelivr.net/ghost/portal@~2.49/umd/portal.min.js" data-i18n="true" data-ghost="https://escape.tech/blog/" data-key="0e4cafc1e55c09b1ec7809b460" data-api="https://escape.tech/blog/ghost/api/content/" data-locale="en" crossorigin="anonymous"></script><style id="gh-members-styles">.gh-post-upgrade-cta-content, .gh-post-upgrade-cta { display: flex; flex-direction: column; align-items: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; text-align: center; width: 100%; color: #ffffff; font-size: 16px; } .gh-post-upgrade-cta-content { border-radius: 8px; padding: 40px 4vw; } .gh-post-upgrade-cta h2 { color: #ffffff; font-size: 28px; letter-spacing: -0.2px; margin: 0; padding: 0; } .gh-post-upgrade-cta p { margin: 20px 0 0; padding: 0; } .gh-post-upgrade-cta small { font-size: 16px; letter-spacing: -0.2px; } .gh-post-upgrade-cta a { color: #ffffff; cursor: pointer; font-weight: 500; box-shadow: none; text-decoration: underline; } .gh-post-upgrade-cta a:hover { color: #ffffff; opacity: 0.8; box-shadow: none; text-decoration: underline; } .gh-post-upgrade-cta a.gh-btn { display: block; background: #ffffff; text-decoration: none; margin: 28px 0 0; padding: 8px 18px; border-radius: 4px; font-size: 16px; font-weight: 600; } .gh-post-upgrade-cta a.gh-btn:hover { opacity: 0.92; }</style> <script defer src="https://cdn.jsdelivr.net/ghost/sodo-search@~1.5/umd/sodo-search.min.js" data-key="0e4cafc1e55c09b1ec7809b460" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search@~1.5/umd/main.css" data-sodo-search="https://escape.tech/blog/" data-locale="en" crossorigin="anonymous"></script> <script defer src="https://cdn.jsdelivr.net/ghost/announcement-bar@~1.1/umd/announcement-bar.min.js" data-announcement-bar="https://escape.tech/blog/" data-api-url="https://escape.tech/blog/members/api/announcement/" crossorigin="anonymous"></script> <link href="https://escape.tech/blog/webmentions/receive/" rel="webmention"> <script defer src="/blog/public/cards.min.js?v=bdd0505571"></script> <link rel="stylesheet" type="text/css" href="/blog/public/cards.min.css?v=bdd0505571"> <script defer src="/blog/public/member-attribution.min.js?v=bdd0505571"></script><style>:root {--ghost-accent-color: #09134b;}</style> <script id="userled-sdk-snippet"> window.userledSettings={app_id:"21ef73bb-cd0a-4f2b-a193-fa051a5974a1"},window.userledSnippetTs=(new Date).getTime(),(function(){if(!window.Userled){window.Userled=function(){return e.call(arguments)};var e=window.Userled;e.call=function(n){return new Promise((function(i,d){e.queue.push([].concat.apply([i,d],n))}))},e.queue=[],e.snippetVersion="4.0.0",window.Userled("page")}})(); </script> <script id="userled-sdk" type="module" src="https://sdk.userledclient.io?appId=21ef73bb-cd0a-4f2b-a193-fa051a5974a1&snippetVersion=4.0.0" data-cfasync="false"></script> <!-- Google tag (gtag.js): Google Ads --> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-234004425-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-234004425-1'); </script> <!-- Global site tag (gtag.js) - Google Analytics --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-0KYN4GPPPE"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-0KYN4GPPPE'); </script> <script> var gh_white_logo = 'https://escape.tech/assets/escape-logo.png'; </script> <!-- Global site tag (gtag.js) - Google Analytics - whole website--> <script async src="https://www.googletagmanager.com/gtag/js?id=G-5FTS8Y5Z4N"></script> <script> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-5FTS8Y5Z4N'); </script> <script> (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'dataLayer', 'GTM-MDMBJH6V'); </script> <!-- End Google Tag Manager --> <!-- Google Tag Manager - blog--> <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','data layer,'GTM-MLMM8LHP');</script> <!-- End Google Tag Manager --> <script> var gh_white_logo = 'https://escape.tech/assets/escape-logo.png'; </script> <!-- Fathom - beautiful, simple website analytics --> <script src="https://cdn.usefathom.com/script.js" data-site="WIHBBNEH" defer></script> <!-- / Fathom --> <!-- Schema --> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://escape.tech/" },{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://escape.tech/blog" }] } </script> <!-- Schema --> <style> .container.large { max-width: calc(750px + 8vw); } </style> <script> var gh_white_logo = "https://i.ibb.co/cx8zN47/ESCAPE-LOGO-28-07-2021-02.png" </script> <!-- Prism.js - syntax highlighting --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism.min.css" integrity="sha512-tN7Ec6zAFaVSG3TpNAKtk4DOHNpSwKHxxrsiw4GHKESGPs5njn/0sMCUMl2svV4wo4BK/rCP7juYz+zx+l6oeQ==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <style> .gh-head { position: -webkit-sticky; /* Older Safari browsers */ position: sticky; /* Most other modern browsers including Safari */ top: 0; z-index: 4000; } /* Shine Case study style */ #strongshine-chose-escape-to-enhance-its-application-security-achieve-a-thorough-inventory-of-their-apis-and-help-developers-fix-issues-quicklystrong, #within-a-week-shine-covered-four-applications-accounting-for-300-endpoints-this-rapid-coverage-led-to-the-discovery-and-fixing-of-at-least-strong2-critical-vulnerabilities-in-less-than-24-hours-post-discoverystrong { width: 65%; font-size: 30px; color: #01e2b7; } @media (max-width: 640px) { #strongshine-chose-escape-to-enhance-its-application-security-achieve-a-thorough-inventory-of-their-apis-and-help-developers-fix-issues-quicklystrong, #within-a-week-shine-covered-four-applications-accounting-for-300-endpoints-this-rapid-coverage-led-to-the-discovery-and-fixing-of-at-least-strong2-critical-vulnerabilities-in-less-than-24-hours-post-discoverystrong { width: 90%; font-size: 1.2em; } } #strongstart-securing-your-apis-for-freestrong { margin-bottom: 0.5em; } .kg-header-card a.kg-header-card-button { display: inline-block !important; padding: 20px 16.5px !important; font-size: 1.4rem !important; font-size: 20px !important; line-height: 0.325em !important; color: #1a1b3d !important; background-color: #05e2b7 !important; border-radius: 6px !important; } </style> </head> <body class="post-template tag-api-security has-sans-body"> <div class="viewport"> <header id="gh-head" class="gh-head"> <nav class="gh-head-inner inner gh-container"> <div class="gh-head-brand"> <a class="gh-head-logo" href="https://escape.tech/blog"> <img width="140" src="https://escape.tech/blog/content/images/2022/05/escape-logo.0e6d59f.svg" alt="Escape - The API Security Blog" /> </a> <div class="mobile-cta-container"> <a style="border-radius: 100px; background-color: #05e2b7; color: #1a1b3d; margin-right: 0; font-weight: 700; font-size: 1.4rem; padding: 12px 18px 13px;" href="https://calendly.com/d/46g-xzy-dgw?utm_source=navbar&utm_medium=blog">Get a demo</a> </div> <a class="gh-burger" role="button"> <div class="gh-burger-box"> <div class="gh-burger-inner"></div> </div> </a> </div> <div class="gh-head-menu"> <ul class="nav"> <li class="nav-escape-platform"><a href="https://escape.tech">Escape Platform</a></li> <li class="nav-case-studies"><a href="https://escape.tech/blog/tag/case-study/">Case studies</a></li> <li class="nav-best-practices"><a href="https://escape.tech/blog/tag/best-practices/">Best Practices</a></li> <li class="nav-events"><a href="https://escape.tech/blog/tag/events/">Events</a></li> <li class="nav-community"><a href="https://join.slack.com/t/escapecommunity/shared_invite/zt-2cpklvqqv-m_h4fzlZhSatxcrxetf3Fg">Community</a></li> </ul> <div class="gh-head-actions"> <a class="try-for-free-button" href="https://calendly.com/d/46g-xzy-dgw?utm_source=navbar&utm_medium=blog">Get a demo</a> <button class="gh-search-icon" aria-label="search" data-ghost-search style="display: inline-flex; justify-content: center; align-items: center; width: 32px; height: 32px; border: 0; color: inherit; background-color: transparent; cursor: pointer; outline: none; margin-left: 4px;"> <svg width="20" height="20" fill="none" viewBox="0 0 24 24"><path d="M14.949 14.949a1 1 0 0 1 1.414 0l6.344 6.344a1 1 0 0 1-1.414 1.414l-6.344-6.344a1 1 0 0 1 0-1.414Z" fill="currentColor"/><path d="M10 3a7 7 0 1 0 0 14 7 7 0 0 0 0-14Zm-9 7a9 9 0 1 1 18 0 9 9 0 0 1-18 0Z" fill="currentColor"/></svg> </button> </div> </div> </nav> </header> <div class="site-content"> <main id="site-main" class="site-main"> <article class="article post tag-api-security image-small"> <header class="article-header gh-canvas"> <section class="article-tag"> <a href="https://escape.tech/blog/tag/api-security/">API Security</a> </section> <h1 class="article-title">Escape's proprietary Business Logic Security Testing algorithm: what makes it innovative</h1> <div class="article-byline"> <section class="article-byline-content"> <ul class="author-list"> <li class="author-list-item"> <a href="/blog/author/antoine/" class="author-avatar"> <img class="author-profile-image" src="/blog/content/images/size/w100/2024/08/Antoine-Escape.jpg" alt="Antoine Carossio" /> </a> </li> </ul> <div class="article-byline-meta"> <h4 class="author-name"><a href="/blog/author/antoine/">Antoine Carossio</a></h4> <div class="byline-meta-content"> <time class="byline-meta-date" datetime="2024-04-17">Apr 17, 2024</time> <span class="byline-reading-time"><span class="bull">•</span> 10 min read</span> </div> </div> </section> </div> <figure class="article-image"> <picture> <!-- Serve the WebP format if the browser supports it --> <source srcset="/blog/content/images/size/w300/format/webp/2024/04/business-logic-algorithm-innovative.png 300w, /blog/content/images/size/w600/format/webp/2024/04/business-logic-algorithm-innovative.png 600w, /blog/content/images/size/w1000/format/webp/2024/04/business-logic-algorithm-innovative.png 1000w, /blog/content/images/size/w2000/format/webp/2024/04/business-logic-algorithm-innovative.png 2000w" sizes="(min-width: 1400px) 1400px, 92vw" type="image/webp" > <!-- Serve original file format as a fallback --> <img srcset="/blog/content/images/size/w300/2024/04/business-logic-algorithm-innovative.png 300w, /blog/content/images/size/w600/2024/04/business-logic-algorithm-innovative.png 600w, /blog/content/images/size/w1000/2024/04/business-logic-algorithm-innovative.png 1000w, /blog/content/images/size/w2000/2024/04/business-logic-algorithm-innovative.png 2000w" sizes="(min-width: 1400px) 1400px, 92vw" src="/blog/content/images/size/w2000/2024/04/business-logic-algorithm-innovative.png" alt="Escape's proprietary Business Logic Security Testing algorithm: what makes it innovative" /> </picture> </figure> </header> <section class="gh-content gh-canvas"> <p>Testing APIs for Business Logic vulnerabilities is hard. Actually, this is a mission that old-school <a href="https://escape.tech/blog/dast-is-dead-why-business-logic-security-testing-takes-center-stage/" rel="noreferrer">DAST solutions like ZAP (formerly OWASP ZAP) cannot handle</a>.</p><p>I'm <a href="https://www.linkedin.com/in/acarossio/?ref=escape.tech" rel="noreferrer">Antoine Carossio</a>, passionate about Computer Science for more than 15 years now and cofounder & CTO of Escape. With my team, we've spent 3 years building a proprietary algorithm capable of <strong>finding Business Logic vulnerabilities in all API types</strong>, especially in the most recent ones like <strong>REST</strong> & <strong>GraphQL</strong>. </p><p>However, simply stating its existence isn't often enough for the security community. </p><p>From security engineers, we constantly hear the same question: <em> How does it work? Are you yet another wrapper of ZAP?</em></p><p>That's why in this popularization article, I'm proud to explain to the world how we created the state-of-the-art Business Logic Security Testing algorithm and <strong>what makes it truly innovative</strong>, so you or your peers don't have to ask this question any longer! </p><h2 id="the-real-challenge-generating-legitimate-traffic-on-apis">The real challenge: generating legitimate traffic on APIs</h2><p>The heart of Escape is inspired by the concept of Feedback-Driven Semantic API Exploration (FDSAE), a Reinforcement Learning algorithm concept introduced by Marina Polishuck, Researcher @ Microsoft <a href="https://www.microsoft.com/en-us/research/uploads/prod/2018/04/restler.pdf?ref=escape.tech" rel="noreferrer">(REST-ler<a href="https://www.microsoft.com/en-us/research/uploads/prod/2018/04/restler.pdf?ref=escape.tech" rel="noreferrer"><strong>)</strong></a></a></p><p>The primary goal of Feedback-Driven Semantic API Exploration (FDSAE) is, first and foremost, to create an algorithm able to generate legitimate sequences of requests that respect the <a href="https://escape.tech/blog/what-is-business-logic-and-why-its-important/" rel="noreferrer">business logic of APIs</a> in a <a href="https://escape.tech/blog/different-types-of-penetration-testing/#black-box" rel="noreferrer"><u>pure black box manner</u></a><u> </u>(<strong>no access to traffic or code</strong>). At that point we do not even talk about security, but about generating traffic.</p><p>Microsoft REST-ler is a fascinating piece of research. As a result, Marina shows how feedback-driven semantic API exploration drastically increases the API coverage and vulnerability findings compared to a mere brute force approach.</p><p>Nonetheless, the limitations of Marina's approach:<br>1. Not suited for CI/CD usage because too long<br>2. Only compatible with REST APIs<br>3. Since then, time has passed, and significant advancements have occurred, including the evolution of artificial intelligence.</p><p>This is why at Escape we decided to start again from scratch to build a revolutionary AI-powered engine that generates traffic, replicating application behavior within the context of business logic. It is built by design for all kinds of modern APIs, including <strong>REST</strong> and <strong>GraphQL</strong>, and does not require any traffic.</p><h2 id="how-does-it-work">How does it work?<br></h2><h3 id="the-metagraph-a-unified-model-to-rule-all-api-standards">The MetaGraph: A unified model to rule all API standards</h3><p>Escape takes as input an API Schema, that can either be automatically generated by Escape from the outside (this will be the topic of another article dedicated to describing how Escape's API Discovery and Inventory works), automatically generated by the API Backend Framework, or manually maintained by developers (Swagger v2, <strong>OpenAPI v3</strong>, <strong>Postman Collection</strong>, Insomnia Collection, WP-JSON, <strong>GraphQL Introspection</strong>, GraphQL Schema, etc.).</p><p>At that point, whatever the underlying technology or input, <strong>the schema is parsed and converted to a representation that is fully agonistic of the underlying API technology</strong>, that’s what we call the <strong>MetaGraph</strong>. We noticed that any API could, in fact, be represented in a Graph structure.</p><p>For the sake of simplicity, we are going to present a simplified version of this <strong>MetaGraph</strong> on a very simple REST API representing the following structure. Although the basic concepts are fully respected, the actual <strong>MetaGraph</strong> structure is, in fact, more granular than the one presented in this article and could be the topic of a more scientific article.</p><p>Let’s consider the following dummy API. Escape uses different hyperparameters depending on whether the database is already seeded or empty. Let’s take the assumption that the database behind this API is empty in our case. </p><p>Our API contains 1 <strong>Resource</strong>:</p><ul><li><code>User</code>, with 2 <strong>Fields</strong>:<ul><li><code>ssn</code>: string</li><li><code>email</code>: string</li></ul></li></ul><p>and <strong>3 Resolvers</strong>:</p><ul><li><code>POST /user</code> <ul><li>2 params in body <ul><li><code>email</code>: string </li><li><code>ssn</code>: string </li></ul></li><li>no return value</li></ul></li><li><code>GET /users</code> <ul><li>no param</li><li>returns a list of <code>User[]</code></li></ul></li><li><code>GET /user/{id}</code> → <code>User</code><ul><li>1 param in path:<ul><li><code>id</code>: string </li></ul></li></ul></li></ul><p>The <strong>MetaGraph</strong> is a bipartite Graph with 2 kinds of nodes: <br>1. All the Resolvers are accessible in the API, here there are 3 <br>2. All the Resources: here 1 <code>User</code></p><p>Each resolver is weighted by a combination of: </p><ul><li>The probability of success that sending an actual request on this resolver with the information leads to a successful request that passes the validation layer</li><li>The potential quantity of information returned by a resolver if sent right now.</li><li>The cycle of creation and access to objects present in the database, which is similar to <code>CRUD</code> for REST APIs.</li></ul><h3 id="sourcing-inference">Sourcing inference</h3><p>At this stage, we have a Graph that contains weighted nodes, but still no edges. <strong>The Graph is also directed</strong>. And there are 2 types of edges:</p><ul><li>Edges from <code>Resolvers</code> to <code>Resources</code>. <ul><li>These are immediately given by the specification. For example, in our case, the 2 <code>GET</code> resolvers are connected to the <code>user</code> object.</li></ul></li><li>And edges from <code>Resources</code> to <code>Resolvers</code>. <ul><li>This is information that is not carried by the schema. Yet, in our case, the <code>GET /users/{id}</code> resolver takes an <code>id</code> as a param in path. This ID actually comes from the <code>user</code> object. This relationship is not explicitly stated anywhere, so it must be inferred. This is what we have called <em><strong>parameter sourcing</strong></em>. In this case, we are out of luck: the API was designed in such a way that the <code>ssn</code> field of the <code>user</code> object is actually documented as being the <code>id</code> parameter of the <code>GET /users/{id}</code> resolver. The semantics are different: how to connect <code>ssn</code> and <code>id</code>? The field could very well have been called <code>id</code> as well. Here, we use a Machine Learning model applied to NLP capable of inferring these relationships in their context, what we have called <em>sourcing inference.</em></li></ul></li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://escape.tech/blog/content/images/2024/04/schema-resolvers-resources.png" class="kg-image" alt="" loading="lazy" width="1600" height="993" srcset="https://escape.tech/blog/content/images/size/w600/2024/04/schema-resolvers-resources.png 600w, https://escape.tech/blog/content/images/size/w1000/2024/04/schema-resolvers-resources.png 1000w, https://escape.tech/blog/content/images/2024/04/schema-resolvers-resources.png 1600w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The MetaGraph, augmented with the Sourcing Inference</span></figcaption></figure><p><strong>Strong typing inference</strong></p><p><strong><em>Sourcing inference</em></strong> is actually completed with a second machine learning algorithm we named <strong><em>typing inference</em></strong>. Indeed, to send requests that are logical and adhere to the API's business logic, <strong>we must ensure the data we send is meaningful</strong>. It would be pointless to send a simple string to the <code>email</code> object when it is expected to be an email address or a simple <code>id</code> to the <code>ssn</code> field when it is in fact a social security number. Therefore, our algorithm supports over 800 different specific data types and includes a<strong> <em>Resolver Parameter</em></strong> <> <strong><em>Resource Field</em> </strong>classification algorithm, which operates in conjunction with <em>sourcing inference</em>. This algorithm considers the entire context of the parameter/field to assign it <strong>the most accurate data type possibl</strong>e.</p><p>To continue with our previous example, the <code>GET /user/{id}</code> query takes a param in path <code>id</code> type <code>string</code> and returns a <code>User</code> object that contains an <code>email</code> field and a <code>ssn</code> field, both in <code>string</code> type.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://escape.tech/blog/content/images/2024/04/CleanShot-2024-04-17-at-16.22.55@2x.png" class="kg-image" alt="" loading="lazy" width="2000" height="1436" srcset="https://escape.tech/blog/content/images/size/w600/2024/04/CleanShot-2024-04-17-at-16.22.55@2x.png 600w, https://escape.tech/blog/content/images/size/w1000/2024/04/CleanShot-2024-04-17-at-16.22.55@2x.png 1000w, https://escape.tech/blog/content/images/size/w1600/2024/04/CleanShot-2024-04-17-at-16.22.55@2x.png 1600w, https://escape.tech/blog/content/images/2024/04/CleanShot-2024-04-17-at-16.22.55@2x.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">How </span><i><em class="italic" style="white-space: pre-wrap;">Typing Inference</em></i><span style="white-space: pre-wrap;"> strongly types the </span><b><strong style="white-space: pre-wrap;">Resolver Parameters</strong></b><span style="white-space: pre-wrap;"> and </span><b><strong style="white-space: pre-wrap;">Resource Fields</strong></b><span style="white-space: pre-wrap;">, already linked together by the </span><i><em class="italic" style="white-space: pre-wrap;">Sourcing Inference</em></i></figcaption></figure><p>In fact, <strong><em>Strong Typing</em></strong> and <strong><em>Sourcing</em></strong> <strong><em>Inference</em></strong> draw three major conclusions:</p><ul><li>The <code>ssn</code> field of the <code>User</code> resource and the <code>id</code> parameter of the <code>GET /user/{id}</code> resolver are connected. When we retrieve a <code>ssn</code>, it will be relevant to reinject it as the <code>id</code> parameter of the <code>GET /user/{id}</code> field in a subsequent request.</li><li>The <code>ssn</code> field of the <code>User</code> object is typed as a string but it is a <code>SOCIAL SECURITY NUMBER</code>. Hence, we type the <code>ssn</code> field more precisely as a social security number.</li><li>Given that the <code>id</code> parameter of the <code>GET /user</code> Resolver is linked to the <code>id</code> field of the <code>user</code> resource, it must also be typed as a social security number.</li></ul><p>Naturally, we also know that the <code>email</code> field of the <code>user</code> object should be typed as an <code>EMAIL</code>, and not a simple string.</p><h3 id="metagraph-initialization">Metagraph Initialization</h3><p>To illustrate our point, let's revisit our example:</p><ul><li>The database is empty. To test the business logic of the API, it's necessary to first populate it with some <code>User</code>s. That's why, during the initiation phase, the <code>POST /user</code> resolver, which takes 2 parameters (<code>email</code>, <code>age</code>), will have the highest weight in the graph and will therefore be sent first. Sending a working request to this resolver first is essential for the other 2 resolvers to make sense.</li><li>The <code>GET /users</code> Resolver does not take any parameters and returns a list of <code>User[]</code>, which, according to sourcing inference, is necessary to have a <code>User</code> <code>id</code> essential for executing a valid request on the <code>GET /user/{id}</code> resolver. It will, therefore, have a higher weight than <code>GET /users/{id}</code>, which takes an <code>id</code> parameter (which is linked to the <code>ssn</code> field of the <code>User</code> resource) and returns a single <code>User</code>.</li></ul><p>Thus, we perfectly understand the dependencies between the types of requests and the different sequences of requests that make sense a priori, even before having sent any request.</p><h3 id="the-reinforcement-iteration">The reinforcement iteration </h3><p>Next, and this is the concept of feedback-driven semantic API exploration, the AI learns iteratively to interact with the API, according to the following reinforcement algorithm:</p><ul><li>Select the path with the best weighting</li><li>Ask the oracle to fuzz it with the most coherent values possible (which respect the inferred types, and the sourcing relations found at initialization)</li><li>Send the request</li><li>Analyze the returned response and store the new information collected, which can be reinjected in the following iterations</li><li>Re-infer the types, dynamically this time to better match the real exchanged types. Without going into details, here we can perform inference on the returned values directly, and not on the documentation context. This notably allows us to detect data leakage.</li><li>Update the weights of our MetaGraph</li></ul><h3 id="what-about-graphql-and-other-kinds-of-apis">What about GraphQL and other kinds of APIs?</h3><p>As mentioned earlier, we managed to build one single abstract model that supports all kinds of APIs at once. If you're interested in how this applies to your APIs, <a href="https://calendly.com/d/46g-xzy-dgw?ref=escape.tech">we'd be happy to walk you through a real-world example</a>. The only pieces of the algorithm that are different are the Schema parsers (in input) and the Client that formats sends requests to the API.</p><p>For example, in GraphQL, the Resources are the <strong>GraphQL Objects</strong>, the Resolvers are all the <strong>non-cyclic Paths of the GraphQL API</strong>, and the whole algorithm is recursive.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://escape.tech/blog/content/images/2024/04/Screenshot-2022-12-09-at-19.36.25.png" class="kg-image" alt="" loading="lazy" width="1600" height="553" srcset="https://escape.tech/blog/content/images/size/w600/2024/04/Screenshot-2022-12-09-at-19.36.25.png 600w, https://escape.tech/blog/content/images/size/w1000/2024/04/Screenshot-2022-12-09-at-19.36.25.png 1000w, https://escape.tech/blog/content/images/2024/04/Screenshot-2022-12-09-at-19.36.25.png 1600w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">A GraphQL request sent by a random DAST (left) vs. Escape (right)</span></figcaption></figure><h3 id="what-about-generative-ai">What about Generative AI?</h3><p>Lately, <a href="https://www.youtube.com/watch?v=E-0mwDOPt88&t=3s&ref=escape.tech" rel="noreferrer">generative AI has been all over the place in the security world</a>. However, its widespread use in discussions within the security vendor space often lacks clear explanations of its implementation. Generative AI enables Escape to send increasingly relevant requests, especially in 2 cases:</p><ol><li><u>Just after initialization</u>: at the very beginning of the algorithm, when we have no requests, we use generative AI to send some particularly relevant initial requests. This allows us to hot start our algorithm.</li><li><u>When receiving Bad Requests</u> (usually <code>400</code> status code for well-designed REST APIs), feeding the error message (sometimes quite verbose!) to an LLM, along with the original request, is particularly relevant to resending the fixed and valid request.</li><li><u>During security tests;</u> some complex attack scenarios require a deep understanding of the business logic context. In some cases, generative AI can make particularly relevant decisions, enabling us to improve the success rate of specific attack scenarios.</li></ol><h2 id="how-do-we-tackle-the-security">How do we tackle the security?</h2><h3 id="security-tests">Security tests</h3><p>Thanks to the preceding algorithm, in addition to having covered the entirety of the API and generated legitimate traffic, we can replay a particular sequence of requests in order to test a specific resolver within a specific context.</p><p>Thus, assessing complex attack scenarios becomes straightforward. Instead of building specific tests for any kind of API, we focus on the abstract flows around the MetaGraph.</p><p>Since our entire DAST (Dynamic Application Security Testing) architecture is event-based, security checks are built as middleware over the exploration algorithm described above.</p><p>We defined 100+ complex attack scenarios on this abstract flow, and now it's possible to create your own in a few lines of YAML code with <a href="https://escape.tech/escape-rules?ref=escape.tech" rel="noreferrer">Escape Rules</a> as well.</p><p>Consider the advantages over static checks like Nuclei or bChecks. As a security professional, you have the capability to create custom security tests for your entire organization with a single click, without requiring ongoing maintenance. It is your <a href="https://escape.tech/blog/new-approach-to-identify-business-logic-flaws/" rel="noreferrer">additional superpower in identifying business logic flaws</a>. Additionally, there's no need to involve your development teams; you can utilize Escape's community to develop these checks for you.</p><h3 id="sensitive-data-detection">Sensitive data detection</h3><p>In addition to the static <em>Strong Typing Inference</em> presented earlier, which is static and only performed at the initialization of the MetaGraph, Escape also performs dynamic inference during the reinforcement iteration loop. Every single response returned by the server is analyzed by a memory-efficient inference algorithm able to detect the precise type of every single data present in the response. This allows Escape to detect more than 800+ data types, including 300+ of critical sensitivity – secrets, tokens, personally identifiable information, etc.</p><p>Static and dynamic inference now makes the task and the results extremely reliable. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://escape.tech/blog/content/images/2024/04/CleanShot-2024-04-17-at-17.55.19@2x.png" class="kg-image" alt="" loading="lazy" width="1876" height="1834" srcset="https://escape.tech/blog/content/images/size/w600/2024/04/CleanShot-2024-04-17-at-17.55.19@2x.png 600w, https://escape.tech/blog/content/images/size/w1000/2024/04/CleanShot-2024-04-17-at-17.55.19@2x.png 1000w, https://escape.tech/blog/content/images/size/w1600/2024/04/CleanShot-2024-04-17-at-17.55.19@2x.png 1600w, https://escape.tech/blog/content/images/2024/04/CleanShot-2024-04-17-at-17.55.19@2x.png 1876w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The complete event-based architecture of the Escape Security Scanner, with the reinforcement loop in the center</span></figcaption></figure><h2 id="benchmark-against-former-technologies">Benchmark against former technologies</h2><p>So what about the results? We invite you to take a look at our <a href="https://escape.tech/blog/dast-tools-benchmark/" rel="noreferrer">benchmark on GraphQL and REST applications against OWASP ZAP and StackHawk</a>.</p><h2 id="conclusion">Conclusion </h2><p>We hope that now we answered your question: how does it work? </p><p>Escape's innovative algorithm, rooted in Feedback-Driven Semantic API Exploration (FDSAE) principles, addresses this complexity by autonomously generating legitimate traffic to test API's business logic. </p><p>Through techniques like <em>Sourcing Inference</em> and <em>Strong Typing Inference</em>, Escape ensures the accuracy of generated requests, while integration with generative AI enhances adaptability, particularly in complex attack scenarios. </p><p>By providing dynamic, event-based security checks aligned with abstract API flows, Escape empowers security professionals to detect and mitigate business logic flaws effectively, while also enabling sensitive data detection. </p><p>And even better than that, Escape offers the ability to create your own <a href="https://escape.tech/blog/introducing-escape-rules/" rel="noreferrer">Custom Security Checks</a>, benefitting from this unique technology, that does not need maintenance over time.</p><p>With Escape’s proprietary technology, you don’t just find security flaws—you prevent them before they become breaches. If you're curious to see Escape in action, we'd be happy to walk you through a quick session—tailored to your APIs! </p><div class="kg-card kg-button-card kg-align-center"><a href="https://calendly.com/d/46g-xzy-dgw?ref=escape.tech" class="kg-btn kg-btn-accent">Book a demo now</a></div><hr><p>💡Want to learn more? Discover the following articles:</p><ul><li><a href="https://escape.tech/blog/dast-is-dead-why-business-logic-security-testing-takes-center-stage/">DAST is dead, why Business Logic Security Testing takes center stage</a></li><li><a href="https://escape.tech/blog/escape-vs-invicti/">Escape vs Invicti</a></li><li><a href="https://escape.tech/blog/bright-security-vs-escape/">Bright Security vs Escape</a></li><li><a href="https://escape.tech/blog/escape-vs-burpsuite/">Escape vs Burp Suite Enterprise</a></li><li><a href="https://escape.tech/blog/top-dast-tools/">2025 Best DAST tools</a></li></ul> </section> </article> </main> <aside class="read-more-wrap"> <div class="read-more inner"> <article class="post-card post "> <a class="post-card-image-link" href="/blog/webinar-recap-the-security-mistakes-everyone-makes-in-m-a/"> <img class="post-card-image" srcset="/blog/content/images/size/w300/2025/02/Webinar---Panels---1--min.png 300w, /blog/content/images/size/w600/2025/02/Webinar---Panels---1--min.png 600w, /blog/content/images/size/w1000/2025/02/Webinar---Panels---1--min.png 1000w, /blog/content/images/size/w2000/2025/02/Webinar---Panels---1--min.png 2000w" sizes="(max-width: 1000px) 400px, 800px" src="/blog/content/images/size/w600/2025/02/Webinar---Panels---1--min.png" alt="Webinar recap: The security mistakes everyone makes in M&A" loading="lazy" /> </a> <div class="post-card-content"> <a class="post-card-content-link" href="/blog/webinar-recap-the-security-mistakes-everyone-makes-in-m-a/"> <header class="post-card-header"> <h2 class="post-card-title">Webinar recap: The security mistakes everyone makes in M&A</h2> </header> <div class="post-card-excerpt"> <p>Discover exactly how to avoid the common security pitfalls during M&A from our panel of experts, who are drawing from decades of experience in the field.</p> </div> </a> <footer class="post-card-meta"> <ul class="author-list"> <li class="author-list-item"> <a href="/blog/author/sanjana/" class="static-avatar"> <img class="author-profile-image" src="/blog/content/images/size/w100/2024/11/me.jpeg" alt="Sanjana Iyer" loading="lazy" /> </a> </li> </ul> <div class="post-card-byline-content"> <span class="post-card-byline-author"><a href="/blog/author/sanjana/">Sanjana Iyer</a></span> <span class="post-card-byline-date"><time datetime="2025-02-12">Feb 12, 2025</time> <span class="bull">•</span> 10 min read</span> </div> </footer> </div> </article> <article class="post-card post "> <a class="post-card-image-link" href="/blog/bright-security-vs-escape/"> <img class="post-card-image" srcset="/blog/content/images/size/w300/2025/02/Escape-vs-Bright-Security.png 300w, /blog/content/images/size/w600/2025/02/Escape-vs-Bright-Security.png 600w, /blog/content/images/size/w1000/2025/02/Escape-vs-Bright-Security.png 1000w, /blog/content/images/size/w2000/2025/02/Escape-vs-Bright-Security.png 2000w" sizes="(max-width: 1000px) 400px, 800px" src="/blog/content/images/size/w600/2025/02/Escape-vs-Bright-Security.png" alt="Bright Security vs Escape" loading="lazy" /> </a> <div class="post-card-content"> <a class="post-card-content-link" href="/blog/bright-security-vs-escape/"> <header class="post-card-header"> <h2 class="post-card-title">Bright Security vs Escape</h2> </header> <div class="post-card-excerpt"> <p>Explore how Bright Security differs from Escape, weigh the advantages and disadvantages of both, and determine the best fit for your company.</p> </div> </a> <footer class="post-card-meta"> <ul class="author-list"> <li class="author-list-item"> <a href="/blog/author/alexandra/" class="static-avatar"> <img class="author-profile-image" src="/blog/content/images/size/w100/2023/09/profile.jpeg" alt="Alexandra Charikova" loading="lazy" /> </a> </li> </ul> <div class="post-card-byline-content"> <span class="post-card-byline-author"><a href="/blog/author/alexandra/">Alexandra Charikova</a></span> <span class="post-card-byline-date"><time datetime="2025-02-07">Feb 7, 2025</time> <span class="bull">•</span> 9 min read</span> </div> </footer> </div> </article> <article class="post-card post "> <a class="post-card-image-link" href="/blog/webinar-recap-sast-and-dast/"> <img class="post-card-image" srcset="/blog/content/images/size/w300/2025/01/SAST-and-DAST-webinar--2-.png 300w, /blog/content/images/size/w600/2025/01/SAST-and-DAST-webinar--2-.png 600w, /blog/content/images/size/w1000/2025/01/SAST-and-DAST-webinar--2-.png 1000w, /blog/content/images/size/w2000/2025/01/SAST-and-DAST-webinar--2-.png 2000w" sizes="(max-width: 1000px) 400px, 800px" src="/blog/content/images/size/w600/2025/01/SAST-and-DAST-webinar--2-.png" alt="Webinar recap: How to combine SAST and DAST for complete application coverage" loading="lazy" /> </a> <div class="post-card-content"> <a class="post-card-content-link" href="/blog/webinar-recap-sast-and-dast/"> <header class="post-card-header"> <h2 class="post-card-title">Webinar recap: How to combine SAST and DAST for complete application coverage</h2> </header> <div class="post-card-excerpt"> <p>Modern applications come with a whole host of challenges that legacy SASTs and DASTs simply cannot keep up with. If you want to be sure no vulnerabilities slip through the cracks in these applications, the key is combining a modern SAST and DAST. Why? Last week, Escape’s CEO Tristan</p> </div> </a> <footer class="post-card-meta"> <ul class="author-list"> <li class="author-list-item"> <a href="/blog/author/sanjana/" class="static-avatar"> <img class="author-profile-image" src="/blog/content/images/size/w100/2024/11/me.jpeg" alt="Sanjana Iyer" loading="lazy" /> </a> </li> </ul> <div class="post-card-byline-content"> <span class="post-card-byline-author"><a href="/blog/author/sanjana/">Sanjana Iyer</a></span> <span class="post-card-byline-date"><time datetime="2025-01-30">Jan 30, 2025</time> <span class="bull">•</span> 16 min read</span> </div> </footer> </div> </article> </div> </aside> </div> <footer class="site-footer outer"> <div class="inner"> <section class="copyright"><a href="https://escape.tech/blog">Escape - The API Security Blog</a> © 2025</section> <nav class="site-footer-nav"> <ul class="nav"> <li class="nav-get-a-demo"><a href="https://calendly.com/d/46g-xzy-dgw">Get a demo</a></li> <li class="nav-escapes-proprietary-business-logic-algorithm"><a href="https://escape.tech/blog/escape-proprietary-algorithm/">Escape's proprietary business logic algorithm</a></li> <li class="nav-best-practices"><a href="https://escape.tech/blog/tag/best-practices/">Best Practices</a></li> <li class="nav-case-studies"><a href="https://escape.tech/blog/tag/case-study/">Case Studies</a></li> <li class="nav-learn-how-to-test-your-graphql-apis"><a href="https://escape.tech/blog/testing-your-graphql-api/">Learn how to test your GraphQL APIs</a></li> <li class="nav-grpc-api-security"><a href="https://escape.tech/blog/how-to-secure-grpc-apis/">gRPC API Security</a></li> <li class="nav-how-to-use-graphql-with-postman"><a href="https://escape.tech/blog/getting-started-with-postman-graphql/">How to use GraphQL with Postman</a></li> <li class="nav-graphql-security"><a href="https://escape.tech/blog/tag/graphql/">GraphQL Security</a></li> <li class="nav-graphql-errors"><a href="https://escape.tech/blog/graphql-errors-the-good-the-bad-and-the-ugly/">GraphQL Errors</a></li> <li class="nav-graphql-armor"><a href="https://escape.tech/graphql-armor/">GraphQL Armor</a></li> <li class="nav-escape-community"><a href="https://join.slack.com/t/escapecommunity/shared_invite/zt-2cpklvqqv-m_h4fzlZhSatxcrxetf3Fg">Escape Community</a></li> <li class="nav-about-us"><a href="https://escape.tech/company/">About Us</a></li> <li class="nav-privacy-policy"><a href="https://escape.tech/privacy/">Privacy Policy</a></li> <li class="nav-api-security-academy"><a href="https://escape.tech/academy/">API Security Academy</a></li> <li class="nav-api-gateway-security-best-practices"><a href="https://escape.tech/blog/api-gateway-security/">API Gateway Security Best Practices</a></li> </ul> </nav> </div> </footer> </div> <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"> </script> <script src="/blog/assets/built/casper.js?v=bdd0505571"></script> <script> $(document).ready(function () { // Mobile Menu Trigger $('.gh-burger').click(function () { $('body').toggleClass('gh-head-open'); }); // FitVids - Makes video embeds responsive $(".gh-content").fitVids(); }); </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/components/prism-core.min.js" integrity="sha512-9khQRAUBYEJDCDVP2yw3LRUQvjJ0Pjx0EShmaQjcHa6AXiOv6qHQu9lCAIR8O+/D8FtaCoJ2c0Tf9Xo7hYH01Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha512-fTl/qcO1VgvKtOMApX2PdZzkziyr2stM65GYPLGuYMnuMm1z2JLJG6XVU7C/mR+E7xBUqCivykuhlzfqxXBXbg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.min.js"></script> <script> tocbot.init({ // Where to render the table of contents. tocSelector: '.gh-toc', // Where to grab the headings to build the table of contents. contentSelector: '.gh-content', // Which headings to grab inside of the contentSelector element. headingSelector: 'h1, h2, h3, h4', }); </script> <!-- Google Tag Manager (noscript) --> <noscript ><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-MDMBJH6V" height="0" width="0" style="display: none; visibility: hidden" ></iframe ></noscript> <!-- End Google Tag Manager (noscript) --> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/prism.min.js" integrity="sha512-hpZ5pDCF2bRCweL5WoA0/N1elet1KYL5mx3LP555Eg/0ZguaHawxNvEjF6O3rufAChs16HVNhEc6blF/rZoowQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/components/prism-yaml.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/components/prism-bash.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/components/prism-jsx.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <!-- Start of HubSpot Embed Code --> <script type="text/javascript" id="hs-script-loader" async defer src="//js-eu1.hs-scripts.com/26857953.js"></script> <!-- End of HubSpot Embed Code --> </body> </html>