<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Matthew Wolfe</title>
    <link>/</link>
    <description>Recent content on Matthew Wolfe</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sun, 15 Jun 2025 00:00:00 +0400</lastBuildDate>
    
        <atom:link href="/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Speedrunning Product: Idea to Revenue in 4 Days</title>
      <link>/post/product/product-speedrun/</link>
      <pubDate>Sun, 15 Jun 2025 00:00:00 +0400</pubDate>
      
      <guid>/post/product/product-speedrun/</guid>
      
        <description>&lt;p&gt;&lt;img src=&#34;/blog/product/timeline.png&#34; alt=&#34;timeline.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Building a revenue-generating SaaS now takes hours, not weeks. That changes the earliest phase of startups.&lt;/p&gt;
&lt;p&gt;I decided to give myself a challenge. &lt;strong&gt;How fast can I turn an idea into a product and get someone to pay me for it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Coming up with an idea was the hardest part. Given that my goal is to earn revenue quickly, taking a &amp;ldquo;solution&amp;rdquo; and trying to find a customer is not a good approach. Instead, the customer and their problem must come first. [1]&lt;/p&gt;
&lt;p&gt;Admittedly, it took me a few weeks of aimlessly learning about topics I found interesting before I came across one worth working on. At some point during this process, I stumbled upon &lt;a href=&#34;https://huggingface.co/nari-labs/Dia-1.6B&#34;&gt;Dia-1.6B&lt;/a&gt;, a new text-to-speech (TTS) model. It supports voice cloning. Could I build a product that does this? [2] After searching the internet, it seems like there are a lot of existing products that do the same thing. I don&amp;rsquo;t see what I have to offer. Moreover, I am concerned about copyright issues.&lt;/p&gt;
&lt;p&gt;While learning more about the TTS model space, I saw a post on X where the guy said he wanted a TTS API specifically to use for an online survival game he was developing. That intrigued me. I slid into Martin&amp;rsquo;s DMs to learn more about his use case.&lt;/p&gt;
&lt;p&gt;Imagine playing a game where every NPC has a deep backstory and a unique personality. Or every player has their own Jarvis/AI assistant to answer questions and provide useful tips. That&amp;rsquo;s something I think should exist.&lt;/p&gt;
&lt;p&gt;So given that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Someone else has this specific need,&lt;/li&gt;
&lt;li&gt;I think this would be awesome,&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I want to do this.&lt;/p&gt;
&lt;h3 id=&#34;day-1-june-7&#34;&gt;Day 1 (June 7)&lt;/h3&gt;
&lt;p&gt;Martin and I discussed the current state of TTS products. Notably, many companies are using this technology for automated customer support. [3] ElevenLabs appears to have the best general-purpose TTS API offering. Their API is nice, but the pricing is ambiguous. Paying for credits upfront and being charged for overages seems to be a turn-off for game developers. Additionally, hyper-realistic voices aren&amp;rsquo;t necessary for most games, especially if building an in-game AI assistant.&lt;/p&gt;
&lt;p&gt;With these ideas in mind, I wanted to put together a landing page that communicates my idea of what a good product in this space would look like. With one prompt, &lt;a href=&#34;https://huggingface.co/spaces/enzostvs/deepsite&#34;&gt;DeepSite&lt;/a&gt; generated the raw HTML for a nice landing page. Most of the design and style seen on the &lt;a href=&#34;https://www.kikashi.io/&#34;&gt;currently deployed landing page&lt;/a&gt; was generated by DeepSite using the following prompt.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;you are a startup founder CTO. Design the landing page for your realtime text-to-speech API for video game developers.
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I have some notion of how to build and deploy a product fast, but I used o3 with Deep Research to sketch out a plan with the full tech stack in order to get another data point.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;You are a startup founder CTO. You have a product idea and want to validate it with potential customers. To do this, you want to build a landing page with a text-to-speech demo and an email waitlist. What tech stack will you build this landing page with? And how can you deploy it as fast as possible without much money?
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I received some follow-up questions, including &lt;code&gt;Should the TTS demo use your own model or can it rely on existing APIs?&lt;/code&gt;. I decided to use the ElevenLabs API for the demo (and eventually the v1 product). There&amp;rsquo;s no sense in spending months developing a custom model if I don&amp;rsquo;t know this is something people are interested in. And willing to pay for it.&lt;/p&gt;
&lt;p&gt;o3 outlined a plan and long justification. I then opened up Cursor. I started a new project and added only the raw HTML from DeepSite into &lt;code&gt;index.html&lt;/code&gt;. I added this to Agent Mode context with Claude 4 and gave the following prompt:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;Using the provided @index.html as the design/template, turn this into a Next.js project using the following plan.
[Insert response from o3]
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In a few minutes, the landing page was mostly implemented. It took some extra prompting to update it with my own branding, fix fonts, and tweak the copy. [4] The generated &lt;code&gt;README&lt;/code&gt; included simple instructions for deploying the project to Vercel. [5] I sent the landing page to Martin and waited for his feedback.&lt;/p&gt;
&lt;p&gt;Without these tools, the same work would have taken me up to a week to complete. The bulk of that time would be spent on design, where my skills are most lacking. DeepSite did this better than I could have done, with one quick prompt. The generated text content was also nicely written and saved me time.&lt;/p&gt;
&lt;h3 id=&#34;day-3-june-9&#34;&gt;Day 3 (June 9)&lt;/h3&gt;
&lt;p&gt;Monday morning, I woke up to a DM from Martin. It was exactly what he was looking for and he signed up for the waitlist. I started working on a usable API for him to work with. Additionally, I learned more useful information from Martin:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;His use case involves each player having their own AI assistant. He uses another LLM to maintain the context of the user&amp;rsquo;s recent actions and respond to their questions on the fly.&lt;/li&gt;
&lt;li&gt;Voice &lt;em&gt;streaming&lt;/em&gt; is important. Waiting for my API to generate the entire audio clip and return it to the client is not feasible. This adds tremendous latency. Time to First Byte (TTFB) is the metric to optimize for.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To solve the first point, I used one of ElevenLabs&#39; built-in robotic voices. To solve the second, the ElevenLabs API already supports streaming. I co-opted my landing page&amp;rsquo;s demo to also support an API endpoint [6]. It took some prompting and manual work to wrap ElevenLabs&#39; Streaming API with my Kikashi streaming API. Claude kept generating code that processed the entire request at once but told me it was streaming chunks. Eventually, it worked.&lt;/p&gt;
&lt;p&gt;After letting Martin try out the API, I got more feedback. He wants a specific voice. He sent me 20 example audio files which ElevenLabs allowed me to easily clone and add to my API.&lt;/p&gt;
&lt;h3 id=&#34;first-revenue-june-10&#34;&gt;First Revenue (June 10)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;/blog/product/mula.png&#34; alt=&#34;mula.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Martin is happy with the API. Some latency issues could be improved, but it&amp;rsquo;s already &lt;em&gt;good enough&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Will you pay me for this?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;d love to.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I sent a payment link for $50/month and Martin paid. [7] Choosing this price was somewhat arbitrary. I chose a fixed pricing model to ease concerns about ambiguous usage-based pricing. The price was chosen because it&amp;rsquo;s more than I&amp;rsquo;m currently paying ElevenLabs. Realistically, I expect two pricing tiers: One for the development phase and another for production. That is something I made up in my mind without talking to any developers or potential customers. The most important thing to keep in mind is that pricing is &lt;strong&gt;difficult&lt;/strong&gt;. I do not know the answer, but I will continue to experiment and see what my customers are willing to pay.&lt;/p&gt;
&lt;h3 id=&#34;reflections&#34;&gt;Reflections&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Could I have done this faster? Definitely. Much of the time I spent, not detailed in this post, was talking to more indie game devs. That didn&amp;rsquo;t help me get revenue quickly, but it did point me toward what I need to work on next. The point of this exercise was not simply to get paid quickly, but to do so in a realistic manner that allows me to turn Kikashi into something more.&lt;/li&gt;
&lt;li&gt;I made an API wrapper. So what? From talking to more game devs, I can see that the major value proposition does not lie strictly in TTS, but rather in joining an agent/brain function with text-to-speech capability. Working with the ElevenLabs API provided me with a quick way to provide minor value and get feedback.&lt;/li&gt;
&lt;li&gt;AI tools enable fast, high-quality code generation. The bottleneck for a lot of companies is no longer building product, but rather knowing what to build, go to market, and growth.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h3&gt;
&lt;p&gt;More broadly, I think it is worth thinking about how viable this is as a business. Learning more about the gaming industry brings rise to some concerns.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Game dev is a hits-based business. Similar to Hollywood, there are few very large studios that make most of the money. Some indie game devs I talked to have been working on the same game for 4+ years without seeing revenue.&lt;/li&gt;
&lt;li&gt;The most common monetization strategy is &amp;ldquo;buy-to-play,&amp;rdquo; where the developer sells their game for a set price and that is it. Popular games might extend this later on with DLCs or microtransactions. The important implication here is that the ongoing cost of maintenance (paying for servers &amp;amp; APIs!) becomes harder to justify for a lot of games.&lt;/li&gt;
&lt;li&gt;Vendor lock-in. It&amp;rsquo;s a scary idea for indie developers to build a game around a third-party API. This is especially true if that API becomes a &lt;em&gt;core&lt;/em&gt; part of the game. Open sourcing parts of the product could help alleviate some of the pain.&lt;/li&gt;
&lt;li&gt;Latency is huge, particularly regarding AI capabilities. That makes the difference between an awesome implementation and a terrible one. I think this can be solved. If companies are already replacing customer support employees with AI, we can probably reduce latency enough to support gaming.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One company whose business I find interesting is &lt;a href=&#34;https://clockworklabs.io/&#34;&gt;Clockwork Labs&lt;/a&gt;, the creators of &lt;a href=&#34;https://spacetimedb.com/&#34;&gt;SpacetimeDB&lt;/a&gt;. Martin&amp;rsquo;s game is built on top of this DB/Server product. It is open source, well-funded (presumably), and generates revenue from hosting the service. This appears to provide a counter-example to most of my concerns.&lt;/p&gt;
&lt;h3 id=&#34;notes&#34;&gt;Notes&lt;/h3&gt;
&lt;p&gt;[1] Solving a specific problem for a specific person is generally a better way to go about deciding what to work on. If you solve the problem well enough, you automatically have your first customer.&lt;/p&gt;
&lt;p&gt;[2] It made me think of my current favourite Instagram account &lt;a href=&#34;https://www.instagram.com/fullstackpeter/&#34;&gt;@fullstackpeter&lt;/a&gt;, which involves a TTS model generating speech that sounds like Peter and Stewie from Family Guy.&lt;/p&gt;
&lt;p&gt;[3] Some great examples are &lt;a href=&#34;https://www.phonely.ai/&#34;&gt;Phonely&lt;/a&gt; and &lt;a href=&#34;https://www.bland.ai/&#34;&gt;Bland&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[4] I still had to update some environment variables with my Supabase, ElevenLabs, and Google Analytics keys. The worst part about that was figuring out how to create a new &amp;ldquo;property&amp;rdquo; in GA. Lesson learned, always use &lt;a href=&#34;https://posthog.com/&#34;&gt;PostHog&lt;/a&gt; for analytics.&lt;/p&gt;
&lt;p&gt;[5] I used a domain I&amp;rsquo;ve been sitting on for a while because I thought it sounded cool. I have too many domain names.&lt;/p&gt;
&lt;p&gt;[6] With a single hard-coded API key.&lt;/p&gt;
&lt;p&gt;[7] First I tried signing up for PayPal and creating a subscription through them. Apparently, the business needs to be approved before that works. Instead, I signed up for Stripe as a sole proprietor and set up the payment link. The process with Stripe took about five minutes. Again, lesson learned.&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>About</title>
      <link>/about/</link>
      <pubDate>Sun, 07 Aug 2022 00:00:00 +0400</pubDate>
      
      <guid>/about/</guid>
      
        <description>&lt;p&gt;Matthew Wolfe is a University of Waterloo alumni, former YC founder, and software engineer.&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>A Simple Ruby Gem Supply Chain Attack</title>
      <link>/post/security/ruby-gem-supply-chain/</link>
      <pubDate>Mon, 08 Aug 2022 00:00:00 +0400</pubDate>
      
      <guid>/post/security/ruby-gem-supply-chain/</guid>
      
        <description>&lt;p&gt;A supply chain attack is where an attacker seeks to exploit an organization by attacking a weaker link in the supply chain which the organization depends on. No organization writes 100% of the code they use in their operations. There exist many external dependencies like open source projects, standard libraries, third-party vendor products, and the hardware these all run on. Each of these vectors can be attacked and so too can the organizations who are dependent on them. There are countless &lt;a href=&#34;https://en.wikipedia.org/wiki/Supply_chain_attack#Examples&#34;&gt;real-world examples&lt;/a&gt; of supply chain attacks where organizations suffer significant damage, including NotPetya (2017), the &lt;a href=&#34;https://en.wikipedia.org/wiki/History_of_Target_Corporation#2013_security_breach&#34;&gt;Target data breach&lt;/a&gt;, and the &lt;a href=&#34;https://en.wikipedia.org/wiki/SolarWinds#2019%E2%80%932020_supply_chain_attacks&#34;&gt;Solarwinds hack&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;motivation&#34;&gt;Motivation&lt;/h2&gt;
&lt;p&gt;Why am I doing this?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I see supply chain attacks as an interesting attack vector with a relatively low bar of technical knowledge required to carry one out. Note that this only applies to specific types, as &amp;ldquo;supply chain attack&amp;rdquo; is a very broad topic.&lt;/li&gt;
&lt;li&gt;I want to explore how an attacker could inject malicious code into an open source project and what exactly the impact could be.&lt;/li&gt;
&lt;li&gt;I want to share this investigation so I can flesh out my half-formed thoughts on the subject.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, I&amp;rsquo;m looking into what the process of attacking the supply chain could look like in a Ruby on Rails application. In particular, Rails apps use external libraries in the form of Gems. I want to see how an attacker could inject a Gem with malicious code to exploit a dependent app.&lt;/p&gt;
&lt;p&gt;All the code for this post can be found under two repos on my GitHub: &lt;a href=&#34;https://github.com/M4THYOU/evil_gem&#34;&gt;the gem&lt;/a&gt; and &lt;a href=&#34;https://github.com/M4THYOU/vulnerable_application&#34;&gt;the Rails app&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-application&#34;&gt;The Application&lt;/h2&gt;
&lt;p&gt;First, we start with a simple Rails application that takes input in a text field and checks if the value is an even number. But since checking an even number is &amp;ldquo;&lt;em&gt;difficult&lt;/em&gt;&amp;rdquo;, we decide to use an existing Gem called &lt;code&gt;evil_gem&lt;/code&gt; to do the work for us.&lt;/p&gt;
&lt;p&gt;We add it to our &lt;code&gt;Gemfile&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span class=&#34;n&#34;&gt;gem&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;evil_gem&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;git&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;git@github.com:M4THYOU/evil_gem.git&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;branch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;and then run &lt;code&gt;bundle install&lt;/code&gt;. Initially, the gem looks fine! It&amp;rsquo;s simply checking if the input is an even number:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;EvenChecker&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;is_even?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;checking if &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; is even...&amp;#34;&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;res&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;even?&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;res&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Then our controller for this page is&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;HomeController&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;ApplicationController&lt;/span&gt;

	&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;index&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;has_key?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
			&lt;span class=&#34;vi&#34;&gt;@query&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
			&lt;span class=&#34;k&#34;&gt;unless&lt;/span&gt; &lt;span class=&#34;vi&#34;&gt;@query&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nil?&lt;/span&gt;
				&lt;span class=&#34;vi&#34;&gt;@res&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;EvenChecker&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_even?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vi&#34;&gt;@query&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
			&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;So, except for the error thrown when the input is not a number, all is well!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/ruby-gem-supply-chain/ex_a.png&#34; alt=&#34;ex_a.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-malicious-actor&#34;&gt;The Malicious Actor&lt;/h2&gt;
&lt;p&gt;But now suppose a malicious actor comes along, call her Mallory, and opens a pull request on &lt;code&gt;evil_gem&lt;/code&gt;. Or phishes the maintainer and takes over their account. Suddenly, Mallory can update the code this Rails application is dependent on.&lt;/p&gt;
&lt;p&gt;Using a crap justification like &amp;ldquo;using OS commands and capturing the output is more performant than Ruby&amp;rsquo;s native &lt;code&gt;even?&lt;/code&gt;, Mallory updates the Gem.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;EvenChecker&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;is_even?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;checking if &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; is even...&amp;#34;&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;res&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`printf &amp;#34;$((&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt; &amp;amp; 1))&amp;#34;`&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nb&#34;&gt;puts&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;res&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Uh oh. Passing user input into backticks in Ruby is bad! The input can escape the quotes and execute arbitrary commands on the system running the app.&lt;/p&gt;
&lt;p&gt;Now that the Gem is updated, the application is not yet vulnerable. The application owner must update the Gem in the project. In this case, the &lt;code&gt;evil_gem&lt;/code&gt;&amp;rsquo;s version is not locked so just running &lt;code&gt;bundle update&lt;/code&gt; will pull in the new malicious code.&lt;/p&gt;
&lt;p&gt;It looks like the app still works:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/ruby-gem-supply-chain/ex_b.png&#34; alt=&#34;ex_b.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;But now let&amp;rsquo;s try a different input:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/ruby-gem-supply-chain/ex_c.png&#34; alt=&#34;ex_c.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;Technically, that is correct. And there is no indication anything went wrong on the user&amp;rsquo;s side. But looking at the server&amp;rsquo;s console we see the output of &lt;code&gt;ls&lt;/code&gt;! The user has successfully injected malicious code into our application via an external dependency and can now use it to exploit the main application. This could be used to steal user data, get access to the web servers, and a lot more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/ruby-gem-supply-chain/ls_output.png&#34; alt=&#34;ls_output.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;prevention&#34;&gt;Prevention&lt;/h2&gt;
&lt;p&gt;How can this be prevented?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use a &lt;code&gt;Gemfile.lock&lt;/code&gt;. Lockfiles ensure your dependencies don&amp;rsquo;t get updated without your explicit permission. If you&amp;rsquo;re secure today, you&amp;rsquo;ll be secure tomorrow.&lt;/li&gt;
&lt;li&gt;Manually review every line of code when you update dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, for the latter suggestion, that isn&amp;rsquo;t practical for most organizations! This would require a certain baseline knowledge of software security and an understanding of how most of that dependency works. This poses a problem because dependencies will often be updated with security fixes of known vulnerabilities so we can&amp;rsquo;t simply skip updates forever.&lt;/p&gt;
&lt;p&gt;Additionally, security issues can still slip by those who have this security and context-specific knowledge. For instance, we might assume in a large open source project, like Rails, that there are already people looking over this code who meet these requirements. But some smaller projects are maintained by much smaller groups who may not know better. Moreover, malicious code can even be &lt;a href=&#34;https://www.theverge.com/2021/4/22/22398156/university-minnesota-linux-kernal-ban-research&#34;&gt;injected into the Linux kernel source&lt;/a&gt; so there is no guarantee that code is safe.&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;The next thing I want to learn more about is how to write software that is safer from attacks on the open source supply chain. Minimize dependencies? Just &lt;a href=&#34;https://asian-power.com/power-utility/commentary/new-paradigm-utility-information-security-assume-your-security-system-has-a&#34;&gt;assume breach&lt;/a&gt; and work from there? What are the best practices? I think the level of security an organization must have in this regard depends on proper threat modeling, but balancing security and business needs is hard to do.&lt;/p&gt;
&lt;p&gt;I think the important takeaway from the investigation is this: You might have the best phishing detection and account takeover protection for your own employees, but that doesn&amp;rsquo;t protect the open source developers or vendors with access to the critical code you depend on.&lt;/p&gt;
</description>
      
    </item>
    
  </channel>
</rss>
