<?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: Abd El-latif</title>
    <description>The latest articles on DEV Community by Abd El-latif (@a-abdellatif98).</description>
    <link>https://dev.to/a-abdellatif98</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F240171%2F6719b654-7bfc-4a3a-8cf2-e392e217c998.JPG</url>
      <title>DEV Community: Abd El-latif</title>
      <link>https://dev.to/a-abdellatif98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/a-abdellatif98"/>
    <language>en</language>
    <item>
      <title>The Security Fix That Would Have Silently Broken My Feature</title>
      <dc:creator>Abd El-latif</dc:creator>
      <pubDate>Tue, 01 Sep 2026 23:24:01 +0000</pubDate>
      <link>https://dev.to/a-abdellatif98/the-security-fix-that-would-have-silently-broken-my-feature-43lm</link>
      <guid>https://dev.to/a-abdellatif98/the-security-fix-that-would-have-silently-broken-my-feature-43lm</guid>
      <description>&lt;p&gt;I recently shipped Mermaid diagram support to &lt;a href="https://github.com/forem/forem" rel="noopener noreferrer"&gt;Forem&lt;/a&gt;, the open source platform that powers DEV. Fenced &lt;code&gt;mermaid&lt;/code&gt; code blocks now render as actual diagrams instead of plain highlighted source.&lt;/p&gt;

&lt;p&gt;Which means this renders, right here, on this post:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[CodeQL flags innerHTML] --&amp;gt; B[Add DOMPurify]
    B --&amp;gt; C{Measure the output}
    C --&amp;gt;|assumed| D[Ship it]
    C --&amp;gt;|actually| E[Labels are gone]
    E --&amp;gt; F[Find the real bug]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The feature itself is not the interesting part. The interesting part is a security alert with an obvious one-line fix, where applying that obvious fix would have quietly destroyed the labels on four of the five most common diagram types. Nothing would have crashed. No test would have failed. The diagrams would just have come out wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The feature, briefly
&lt;/h2&gt;

&lt;p&gt;Forem renders Markdown through Redcarpet with a Rouge highlighter. Rouge already receives the language hint, so intercepting &lt;code&gt;block_code&lt;/code&gt; is enough to emit different markup for Mermaid. The browser then renders it.&lt;/p&gt;

&lt;p&gt;Two things bit me before I ever got to the security question, and both are worth knowing if you contribute to Forem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha one: the sanitizer eats your class
&lt;/h2&gt;

&lt;p&gt;The natural markup is &lt;code&gt;&amp;lt;pre class="mermaid"&amp;gt;&lt;/code&gt;, which is what Mermaid's own documentation uses. On Forem it does not work, and it fails in the most annoying way possible: silently.&lt;/p&gt;

&lt;p&gt;Every piece of rendered Markdown passes through a scrubber with an allow list, and &lt;code&gt;class&lt;/code&gt; is not on it. The attribute is stripped before the HTML ever reaches the browser, so the client side script finds nothing and no error is raised anywhere. You are left staring at correct-looking server output and a page that does nothing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;data-lang&lt;/code&gt; &lt;strong&gt;is&lt;/strong&gt; on the allow list. So the markup becomes &lt;code&gt;&amp;lt;pre data-lang="mermaid"&amp;gt;&lt;/code&gt; and the script targets &lt;code&gt;pre[data-lang="mermaid"]&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;This also settled a design question. Rendering the SVG on the server would avoid shipping a JavaScript library at all, and the allowed tag list does include &lt;code&gt;svg&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;g&lt;/code&gt;, so at a glance it looks viable. It is not. The list is missing &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;tspan&lt;/code&gt;, &lt;code&gt;foreignObject&lt;/code&gt;, &lt;code&gt;marker&lt;/code&gt; and &lt;code&gt;style&lt;/code&gt;, which is to say all the labels, all the arrowheads and all the styling. Server rendered diagrams would have arrived mangled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha two: your diagram source is not safe from the emoji parser
&lt;/h2&gt;

&lt;p&gt;Forem post-processes rendered HTML. Among other things it converts &lt;code&gt;:smile:&lt;/code&gt; into an emoji and turns &lt;code&gt;@name&lt;/code&gt; into a profile link. Both of those are perfectly reasonable for prose and actively destructive for diagram source, where a sequence diagram label might legitimately contain either.&lt;/p&gt;

&lt;p&gt;Both post-processors skip &lt;code&gt;code&lt;/code&gt; nodes. So the source goes inside a &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; wrapper within the &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt;, which costs nothing on the client because &lt;code&gt;textContent&lt;/code&gt; reads through it anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now the interesting part
&lt;/h2&gt;

&lt;p&gt;With the feature working and the pull request open, GitHub's CodeQL analysis flagged a line:&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;// CodeQL: DOM text reinterpreted as HTML&lt;/span&gt;
&lt;span class="nx"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The taint path is real and easy to state. Diagram source is written by users. It flows into &lt;code&gt;mermaid.render()&lt;/code&gt;. What comes back is assigned to &lt;code&gt;innerHTML&lt;/code&gt;. User input reaches the DOM as markup.&lt;/p&gt;

&lt;p&gt;There is a defence already in place. Mermaid runs with &lt;code&gt;securityLevel: 'strict'&lt;/code&gt;, under which it sanitizes its own output with DOMPurify. I do not believe the code was exploitable as written. But relying entirely on a third party library's internal sanitization for untrusted input is exactly the pattern behind Mermaid's own historical XSS advisories, so leaning on it felt like the wrong answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  The obvious fix
&lt;/h3&gt;

&lt;p&gt;Sanitize explicitly. This is a two line change and it is what almost every article on this class of finding will tell you to 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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DOMPurify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dompurify&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;SANITIZE_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;USE_PROFILES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;svg&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;svgFilters&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="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DOMPurify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sanitize&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="nx"&gt;SANITIZE_CONFIG&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CodeQL goes green. The taint path is broken. Ship it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened when I measured
&lt;/h3&gt;

&lt;p&gt;Before committing, I wanted to confirm the sanitizer was transparent, meaning it removed nothing legitimate. So I rendered a handful of diagrams, sanitized the output, and compared element counts on each side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[flowchart] LOST div:5-&amp;gt;1, foreignobject:4-&amp;gt;0, span:4-&amp;gt;0, p:4-&amp;gt;0
[sequence]  nothing lost
[class]     LOST div:4-&amp;gt;1, foreignobject:3-&amp;gt;0, span:3-&amp;gt;0, p:3-&amp;gt;0
[state]     LOST div:3-&amp;gt;1, foreignobject:2-&amp;gt;0, span:2-&amp;gt;0, p:2-&amp;gt;0
[er]        LOST div:3-&amp;gt;1, foreignobject:2-&amp;gt;0, span:2-&amp;gt;0, p:2-&amp;gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DOMPurify was stripping every &lt;code&gt;foreignObject&lt;/code&gt;, and with it the &lt;code&gt;div&lt;/code&gt;, &lt;code&gt;span&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt; nodes inside. Those elements are how Mermaid draws HTML labels. The diagrams would still have rendered. Boxes, arrows and layout all intact. Just no text in them, on four of the five types I tested.&lt;/p&gt;

&lt;p&gt;DOMPurify is not being unreasonable here. &lt;code&gt;foreignObject&lt;/code&gt; lets you embed HTML inside SVG, which makes it a namespace confusion vector, so DOMPurify removes it and will keep removing it whatever profile you pass. My own attack probe confirmed why that matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script         -&amp;gt; &amp;lt;svg&amp;gt;&amp;lt;text&amp;gt;hi&amp;lt;/text&amp;gt;&amp;lt;/svg&amp;gt;
onload         -&amp;gt; &amp;lt;svg&amp;gt;&amp;lt;g&amp;gt;&amp;lt;text&amp;gt;hi&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;&amp;lt;/svg&amp;gt;
onerror        -&amp;gt; &amp;lt;svg&amp;gt;&amp;lt;image href="x"&amp;gt;&amp;lt;/image&amp;gt;&amp;lt;/svg&amp;gt;
javascript:    -&amp;gt; &amp;lt;svg&amp;gt;&amp;lt;a&amp;gt;&amp;lt;text&amp;gt;hi&amp;lt;/text&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/svg&amp;gt;
foreignObject  -&amp;gt; &amp;lt;svg&amp;gt;&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line is the payload that only got neutralised &lt;strong&gt;because&lt;/strong&gt; &lt;code&gt;foreignObject&lt;/code&gt; was removed. Adding it back to an allow list to save my labels would have reopened the exact hole I was trying to close.&lt;/p&gt;

&lt;h3&gt;
  
  
  The actual root cause
&lt;/h3&gt;

&lt;p&gt;At this point the obvious conclusion is that sanitizing and supporting labels are in conflict. They are not. The real question is why &lt;code&gt;foreignObject&lt;/code&gt; was in the output at all, because I had already configured Mermaid to avoid HTML labels:&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;mermaid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;securityLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;flowchart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;htmlLabels&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="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;htmlLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those nested settings do nothing. I only found out by counting elements in the rendered output rather than trusting the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nested flowchart.htmlLabels: false    foreignObject=3  text=2
top-level htmlLabels: false           foreignObject=0  text=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set at the top level, &lt;code&gt;htmlLabels&lt;/code&gt; works, and labels come out as real SVG &lt;code&gt;text&lt;/code&gt; nodes. Two labels became five, because the three that had been hidden inside &lt;code&gt;foreignObject&lt;/code&gt; wrappers are now first class SVG.&lt;/p&gt;

&lt;p&gt;So the configuration I believed was hardening the renderer had been silently inert the whole time. The security fix did not create that bug. It exposed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;htmlLabels&lt;/code&gt; corrected, sanitization removes nothing at all. I verified across every diagram type I could think of by comparing tag counts before and after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart  foreignObject=0  lossless
sequence   foreignObject=0  lossless
class      foreignObject=0  lossless
state      foreignObject=0  lossless
er         foreignObject=0  lossless
gantt      foreignObject=0  lossless
pie        foreignObject=0  lossless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two problems, one fix. The renderer no longer emits an element class that is a known XSS vector, and the explicit sanitizer can run without costing anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took away
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A sanitizer is a transformation, not a no-op.&lt;/strong&gt; Adding one changes your output. If you do not diff before and after, you are guessing about what it took away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Config that fails silently is worse than config that errors.&lt;/strong&gt; &lt;code&gt;flowchart.htmlLabels&lt;/code&gt; was accepted, ignored, and reported nothing. It looked like hardening and did nothing at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static analysis findings are worth investigating, not just closing.&lt;/strong&gt; CodeQL pointed at a line that was probably fine. Chasing it properly surfaced a real defect two layers down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The dangerous bugs are the quiet ones.&lt;/strong&gt; A crash gets fixed in an hour. Diagrams that render with missing labels ship, and then sit there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The feature is live
&lt;/h2&gt;

&lt;p&gt;The pull request was merged into Forem on August 25, 2026. It was reviewed and merged by Ben Halpern, who founded DEV, and I will admit the review comment made my week.&lt;/p&gt;

&lt;p&gt;Every Mermaid block in this post is rendering through that code right now, no image export step required, which was the whole point: people cross-posting from a static blog no longer have to maintain a separate version of every article.&lt;/p&gt;

&lt;p&gt;If you want to read the implementation, it is &lt;a href="https://github.com/forem/forem/pull/23764" rel="noopener noreferrer"&gt;pull request #23764&lt;/a&gt;, against &lt;a href="https://github.com/forem/forem/issues/23671" rel="noopener noreferrer"&gt;issue #23671&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Tired of Rails one-off scripts becoming a nightmare? I built something for that.</title>
      <dc:creator>Abd El-latif</dc:creator>
      <pubDate>Mon, 17 Nov 2025 21:51:23 +0000</pubDate>
      <link>https://dev.to/a-abdellatif98/tired-of-rails-one-off-scripts-becoming-a-nightmare-i-built-something-for-that-5fcb</link>
      <guid>https://dev.to/a-abdellatif98/tired-of-rails-one-off-scripts-becoming-a-nightmare-i-built-something-for-that-5fcb</guid>
      <description>&lt;p&gt;You know the drill. You need to run a script once to fix some data, update user preferences, or migrate something. You write a rake task, run it, and then... did it actually run? Did it work? If something breaks halfway through, how do you know where to restart?&lt;/p&gt;

&lt;p&gt;I got tired of this cycle, so I built &lt;strong&gt;script_tracker&lt;/strong&gt; - a Ruby gem that treats your one-off scripts like migrations (but better).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracks which scripts have run (no more "did I run this already?")&lt;/li&gt;
&lt;li&gt;Wraps everything in transactions (rollback on failure)
&lt;/li&gt;
&lt;li&gt;Built-in progress logging ("Processing 1,247 of 10,000 users...")&lt;/li&gt;
&lt;li&gt;Batch processing helpers (because memory matters)&lt;/li&gt;
&lt;li&gt;Timeout support (no more runaway scripts)&lt;/li&gt;
&lt;li&gt;Simple rake commands to manage everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&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="c1"&gt;# Some random rake task&lt;/span&gt;
&lt;span class="c1"&gt;# Did this run? Who knows!&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="ss"&gt;:fix_user_preferences&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&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="c1"&gt;# Clean, tracked, logged&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fix user preferences"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="ss"&gt;:run&lt;/span&gt;
&lt;span class="n"&gt;rake&lt;/span&gt; &lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;  &lt;span class="c1"&gt;# See what ran and when&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best part? If your script fails halfway through, you know exactly where, and you can handle retries properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I built this:&lt;/strong&gt;&lt;br&gt;
Data scripts have burned every Rails dev. When you run something in production, it fails silently, or worse, it runs twice and corrupts the data. I wanted the same confidence we have with migrations, but for one-off scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real talk:&lt;/strong&gt; This started as internal tooling at my company. We had too many “wait, did that script run?” conversations. Now our data migrations are as reliable as our schema migrations.&lt;/p&gt;

&lt;p&gt;The gem is open source and ready to use. Would love feedback from fellow Rails developers who’ve felt this pain.&lt;/p&gt;

&lt;p&gt;Check it out: &lt;a href="https://github.com/a-abdellatif98/script_tracker" rel="noopener noreferrer"&gt;https://github.com/a-abdellatif98/script_tracker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What’s your biggest one-off script horror story? I bet this would have prevented it.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
  </channel>
</rss>
