<?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: Fernando Gallardo</title>
    <description>The latest articles on DEV Community by Fernando Gallardo (@jfgg).</description>
    <link>https://dev.to/jfgg</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%2F3986182%2F53cecff7-4a58-4a64-970a-374962f8996f.jpeg</url>
      <title>DEV Community: Fernando Gallardo</title>
      <link>https://dev.to/jfgg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jfgg"/>
    <language>en</language>
    <item>
      <title>30+ confirmed vulnerabilities in production OSS, at $6–7 per full codebase scan</title>
      <dc:creator>Fernando Gallardo</dc:creator>
      <pubDate>Thu, 02 Jul 2026 20:49:05 +0000</pubDate>
      <link>https://dev.to/jfgg/30-confirmed-vulnerabilities-in-production-oss-at-6-7-per-full-codebase-scan-11fh</link>
      <guid>https://dev.to/jfgg/30-confirmed-vulnerabilities-in-production-oss-at-6-7-per-full-codebase-scan-11fh</guid>
      <description>&lt;p&gt;We're not naming the platforms yet. Several findings are still in responsible disclosure, some without a confirmed patch. But we can share the methodology, the numbers, and enough about the findings to make this worth reading.&lt;/p&gt;

&lt;p&gt;Over the past several months we ran our system against real open-source projects in production, used by thousands of organizations, across 7 programming languages. Result: 30+ confirmed real vulnerabilities, with at least one independently corroborated by a security researcher who has no connection to us. Here's how it works and what we actually found.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;The system has two main components.&lt;/p&gt;

&lt;p&gt;Structural code modeling. Not text pattern matching, a graph that combines syntax, control flow, and data flow across the entire codebase. This is what lets the system reason about how files interact with each other, not just what's inside each file in isolation. Most vulnerabilities in real codebases don't live in a single function; they emerge from the interaction between components.&lt;/p&gt;

&lt;p&gt;AI agents that reason like an attacker. For each candidate location flagged by the structural model, an agent generates a concrete exploitation hypothesis, then actively searches the code for evidence to disprove it. A finding only gets reported if the agent can't rule it out.&lt;/p&gt;

&lt;p&gt;The hallucination problem in AI-based vulnerability detection is real and well-documented. Our answer to it is a three-layer independent verification pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Layer 1: Agent generates the attack hypothesis.&lt;/li&gt;
&lt;li&gt;Layer 2: An independent analyst agent — with more context and access to surrounding code — whose sole job is to attempt to disprove Layer 1's finding.&lt;/li&gt;
&lt;li&gt;Layer 3: Literal factual verification using a model from a different provider than the one that generated the original finding. It checks every specific claim, line numbers, function names, actual values against the source code, word for word.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Layer 3 already caught a real false positive in production that Layers 1 and 2 had passed as confirmed. The false positive was caused by a variable reassignment several function calls up the chain that neither earlier pass had followed far enough. That's the kind of thing that makes AI vulnerability reports unreliable in practice and the reason we built the verification architecture the way we did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prioritization engine: why the cost is so low
&lt;/h2&gt;

&lt;p&gt;The $6–7 figure per full codebase scan comes from a specific architectural decision: the expensive AI reasoning only runs on a small fraction of the codebase.&lt;/p&gt;

&lt;p&gt;The first pass is a deterministic prioritization engine, no LLM / no API cost, that scores every function in the repo based on structural signals: data flow complexity, external input handling, memory management patterns, interaction with authentication or cryptographic primitives, and similar heuristics. This runs fast and cheap. Its job is to filter out the 98–99% of code that almost certainly isn't worth deep analysis.&lt;/p&gt;

&lt;p&gt;Only the functions that score above a threshold go to the AI reasoning layers. Here's what that looked like across three real runs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu0jj0umqi4ok4jy0szj3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu0jj0umqi4ok4jy0szj3.png" alt="Project Cost" width="673" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Project B's higher percentage (17.4%) is an outlier, that codebase had a higher density of functions touching external input directly. The 0.4–0.9% range on the other two is more representative.&lt;/p&gt;

&lt;p&gt;For comparison: a thorough manual security review runs at roughly 100–150 LOC/hour per specialist, by standard industry estimates. Project A would take approximately 1,900 human-hours at that rate around 48 weeks of full-time work. We're not claiming this is a controlled equivalence; a human expert prioritizes differently and wouldn't attempt 100% coverage of a repo that size. But the system does run 100% of the code through the prioritization engine before deciding what to go deep on, the coverage is systematic, not intuition-based.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blind CVE reproduction as a reliability test
&lt;/h2&gt;

&lt;p&gt;Before running against unknown targets, we validated the system's reliability by giving it codebases with known vulnerabilities it hadn't seen without telling it where the vulnerability was.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 1&lt;/strong&gt;: Pre-patch version of a widely-used JWT/authentication library, with a public CVE for an algorithm confusion vulnerability. The system found it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 2&lt;/strong&gt;: Pre-disclosure version of infrastructure used across a large part of the AI application industry, with a critical pre-authentication SQL injection. The system found it. According to public reports, this same vulnerability was actively exploited within 36 hours of its official public disclosure.&lt;/p&gt;

&lt;p&gt;These weren't needle-in-a-haystack scenarios designed to make the system look good — they were real codebases with the full complexity of production code, and the system found the right thing without being pointed at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we found in the wild
&lt;/h2&gt;

&lt;p&gt;Running against the latest public versions of several critical projects with no known CVE going in:&lt;/p&gt;

&lt;p&gt;Heap buffer overflow in a widely-deployed, low-level infrastructure component. Found autonomously, without a prior hint. An independent security researcher, with no connection to us, reported the exact same bug at nearly the same time. The affected platform cross-validated both reports in their triage process. We can't inflate that validation, it was an external third party reaching the same conclusion independently.&lt;/p&gt;

&lt;p&gt;JWT signature verification vulnerability without a prior CVE, in the latest public version of a widely-used piece of AI infrastructure.&lt;/p&gt;

&lt;p&gt;Multiple vulnerabilities in a widely-adopted local AI runtime,  memory exhaustion DoS, cryptographic integrity bypass, and command injection — currently in responsible disclosure.&lt;/p&gt;

&lt;p&gt;Total across all runs and languages: 30+ confirmed vulnerabilities, all passing through the three-layer verification pipeline before being reported.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't do
&lt;/h2&gt;

&lt;p&gt;The system doesn't replace the security specialist making the final call. Every finding comes with a concrete attack hypothesis and specific evidence, but a human still decides what's real, what's in scope, and what gets reported. What changes is what that person spends their time on: they get a short list of findings with context, not thousands of functions to read through hoping something stands out.&lt;/p&gt;

&lt;p&gt;We're not claiming zero false positives, the three-layer architecture exists precisely because we expect some to get through, and we're still refining based on patterns we've identified in production. What we are claiming is that the verification pipeline makes false positives significantly harder to sustain through all three layers than in a single-pass system.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Happy to answer questions about the architecture, the prioritization heuristics, or specifics on any of the finding categories. We'll share more on the individual vulnerabilities as disclosures are confirmed.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Vibe coding isn't the problem. Volume is.</title>
      <dc:creator>Fernando Gallardo</dc:creator>
      <pubDate>Tue, 30 Jun 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/jfgg/vibe-coding-isnt-the-problem-volume-is-f6g</link>
      <guid>https://dev.to/jfgg/vibe-coding-isnt-the-problem-volume-is-f6g</guid>
      <description>&lt;p&gt;The debate around vibe coding has settled into a predictable shape: people who write code with AI assistance are reckless, people who don't are careful, and the fix is to get more humans back in the loop. It's a comfortable narrative because it has a villain. It's also wrong, or at least pointed at the wrong target.&lt;/p&gt;

&lt;p&gt;A senior engineer using Copilot or Cursor ships the same category of vulnerable code as someone vibe coding their first SaaS product. Not because skill doesn't matter, it does, for design decisions, architecture, knowing what to build. But for a specific class of security bug, skill level is almost irrelevant. The model suggesting the code was trained on a snapshot of the world that's already out of date the moment it ships, and it's generating that code at a volume no human review process was built to handle.&lt;/p&gt;

&lt;p&gt;That's the actual problem, Not who's typing, How much code is being produced, and how fast the threat landscape underneath it moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The training data is always behind the threat
&lt;/h2&gt;

&lt;p&gt;Every LLM used for code generation has a training cutoff. Everything it knows about secure patterns, vulnerable dependencies, and best practices is frozen at that point. New CVEs get published daily, the OSV database adds entries continuously, a package that was safe at training time can have a critical vulnerability disclosed the next week and the model has no way to know that.&lt;/p&gt;

&lt;p&gt;This isn't a hypothetical edge case, It's the default state of every AI-assisted codebase. The model will confidently suggest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# commonly suggested, rarely flagged
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;verify=False&lt;/code&gt; disables TLS certificate verification. It shows up constantly in tutorials and Stack Overflow answers from years ago, exactly the kind of pattern that gets absorbed into training data and reproduced without context. The model isn't being careless, it's reproducing what it learned, and what it learned has a shelf life.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fghgok88zxnuvzq09qkne.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fghgok88zxnuvzq09qkne.png" alt="Stack Overflow answers" width="800" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same applies to dependency versions. A model trained six months ago might recommend a package version with a known CVE disclosed in the meantime, with no signal to the developer that anything's wrong. The code runs, the tests pass and the vulnerability ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real shift is volume, not intent
&lt;/h2&gt;

&lt;p&gt;Here's what actually changed in the last two years: the amount of code a single developer can produce went up by an order of magnitude. That's true whether they're using AI deliberately and carefully, or vibe coding an entire app over a weekend.&lt;/p&gt;

&lt;p&gt;Code review processes, the ones most teams still use, were designed for a world where a senior engineer could reasonably read every line of every PR. That assumption breaks down when PRs are 5x larger and arrive 3x more often. Nobody decided to skip security review. The math of human attention just stopped working.&lt;/p&gt;

&lt;p&gt;This is the part the vibe coding debate misses entirely. The conversation about "is it okay to ship code you don't fully understand" is a separate, legitimate question. But even code written by someone who understands every line is vulnerable to the same training-data lag, and produced at the same volume that overwhelms manual review. Blaming vibe coding treats the symptom and ignores the mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  What manual review can't keep up with
&lt;/h2&gt;

&lt;p&gt;Consider what's actually required to catch a vulnerability introduced by AI-suggested code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Know the current CVE landscape, not what was true at training time, but what's true today&lt;/li&gt;
&lt;li&gt;Trace data flow across files, not just read the diff in isolation&lt;/li&gt;
&lt;li&gt;Check every new or updated dependency against live vulnerability databases (OSV, NVD, GitHub Advisories)&lt;/li&gt;
&lt;li&gt;Do this at the speed code is being merged, not in a quarterly audit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A human reviewer can do all four. They cannot do all four, accurately, for every PR, every day, across a growing codebase, without it becoming their entire job. That's not a knock on engineers, it's a description of what the volume of AI-generated code actually demands.&lt;/p&gt;

&lt;p&gt;This is where the conversation needs to shift from "should we trust AI to write code" to "&lt;strong&gt;&lt;em&gt;how do we close the gap between code velocity and security review velocity.&lt;/em&gt;&lt;/strong&gt;" Those are different problems with different solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the gap requires matching the speed, not adding more humans
&lt;/h2&gt;

&lt;p&gt;The instinct to fix this with more manual review doesn't scale, you'd need to grow your security headcount proportionally to your AI-assisted output, which defeats the purpose of using AI assistance in the first place.&lt;/p&gt;

&lt;p&gt;What actually works is tooling that operates at the same speed code is generated, cross-referencing against vulnerability data that updates daily instead of training data that's frozen. This means scanning for vulnerable dependency versions against live databases, not static rule sets. It means analyzing the full project graph instead of isolated diffs, since vulnerabilities frequently emerge from how files interact rather than from any single file in isolation. And it means flagging deprecated or insecure patterns the moment they're introduced, not in a retrospective audit weeks later.&lt;/p&gt;

&lt;p&gt;This is the gap &lt;a href="https://ixtli.app" rel="noopener noreferrer"&gt;Ixtli&lt;/a&gt; is built to close, normalizing OSV, NVD, and GitHub Advisories into a single engine that checks every PR against what's known today, not what was known when the model generating the code was trained. The point isn't to slow down AI-assisted development. It's to make the review process move at the same speed the code does.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for your workflow
&lt;/h2&gt;

&lt;p&gt;If you're shipping AI-assisted code, and at this point, almost everyone is, whether deliberately or as a byproduct of using modern editors, the relevant question isn't whether to trust the AI. It's whether your review process can keep pace with what it's producing.&lt;/p&gt;

&lt;p&gt;A few things worth checking regardless of how your team writes code:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;th&gt;Why training-data lag makes it worse&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Outdated dependency versions&lt;/td&gt;
&lt;td&gt;Model recommends packages safe at training time, vulnerable now&lt;/td&gt;
&lt;td&gt;Scan against live CVE databases on every PR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deprecated security patterns&lt;/td&gt;
&lt;td&gt;Old patterns (e.g. &lt;code&gt;verify=False&lt;/code&gt;) persist in training data&lt;/td&gt;
&lt;td&gt;Flag at commit time, not in quarterly audits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-file vulnerabilities&lt;/td&gt;
&lt;td&gt;Diff-based review misses interaction effects&lt;/td&gt;
&lt;td&gt;Analyze full dependency graph, not isolated changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volume exceeding reviewer bandwidth&lt;/td&gt;
&lt;td&gt;More code, same number of reviewers&lt;/td&gt;
&lt;td&gt;Automate the first pass; humans review what's flagged&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The skill of the person writing the code matters less here than most people assume. What matters is whether the system around that code can verify it against reality as it exists today, not as it existed when the model was trained.&lt;/p&gt;

&lt;p&gt;If your team is shipping faster than your review process can verify, that gap doesn't close itself, it just gets wider as the codebase grows. Worth checking before it becomes the kind of thing you find out about from a security researcher instead of a teammate.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A single XSS can kill your startup</title>
      <dc:creator>Fernando Gallardo</dc:creator>
      <pubDate>Tue, 30 Jun 2026 01:51:07 +0000</pubDate>
      <link>https://dev.to/jfgg/a-single-xss-can-kill-your-startup-4k41</link>
      <guid>https://dev.to/jfgg/a-single-xss-can-kill-your-startup-4k41</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgic6nnixbjc3tqp22w0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgic6nnixbjc3tqp22w0.png" alt="A security discovery made by Ixtli and discussed in the PR stream" width="799" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A single XSS can kill your startup
&lt;/h2&gt;

&lt;p&gt;Everyone talks about churn from bad UX, slow load times, confusing onboarding, a button that doesn't work on mobile. The discourse around "losing users" is almost entirely product-focused.&lt;/p&gt;

&lt;p&gt;Nobody talks about what happens when your update endpoint serves a &lt;code&gt;javascript:&lt;/code&gt; URI to every user who checks for a new version. Or when your release notes field, the one that accepts markdown, gets used to inject a script tag into every client that renders it. Or when your static release token has no rate limiting, so an attacker has unlimited attempts to brute-force it.&lt;/p&gt;

&lt;p&gt;These aren't theoretical scenarios, they're the kinds of findings that show up in a routine PR audit on a Tool version management system. And unlike a broken modal or a confusing signup flow, you don't get a Hotjar recording that tells you something went wrong. You get a breach report or you get silence, and find out six months later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The vulnerability that doesn't look like one
&lt;/h2&gt;

&lt;p&gt;Here's what a real pull request looked like recently, flagged during a security audit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArtifactCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;arch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;  &lt;span class="c1"&gt;# accepts any URI
&lt;/span&gt;    &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four fields, No validation, The url field accepts &lt;code&gt;https://&lt;/code&gt; but it also accepts &lt;code&gt;javascript:alert(document.domain)&lt;/code&gt;, &lt;code&gt;data:text/html&lt;/code&gt;,&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;..., and &lt;code&gt;http://internal-svc:8080/admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When your Tool auto-update endpoint returns this URL and a client renders it, that's XSS. When your own Tool follows the URL to download a binary, that's SSRF and depending on your cloud setup, that could mean access to internal services, metadata endpoints, or worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix is four lines:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpUrl&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArtifactCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linux&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;darwin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;windows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;arch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amd64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arm64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpUrl&lt;/span&gt;
    &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the point isn't the fix. The point is that this code shipped, reviewed, merged, deployed because nobody was looking at it from a security perspective. The developer wasn't careless, they were moving fast. &lt;br&gt;
That's the context in which most startup code gets written.&lt;/p&gt;
&lt;h2&gt;
  
  
  What a compromised release token actually costs you
&lt;/h2&gt;

&lt;p&gt;The same PR had a second issue: a static TOOL_RELEASE_TOKEN with no rate limiting on the authentication endpoint and no rotation mechanism.&lt;/p&gt;

&lt;p&gt;Walk through what that means in practice:&lt;/p&gt;

&lt;p&gt;An attacker brute-forces the token, no lockout, no throttle, unlimited attempts. They now have the ability to publish a fake Tool version with a malicious download URL. &lt;br&gt;
Your users run tool update, they download and execute a binary from an attacker-controlled server and every machine that auto-updated is now compromised.&lt;/p&gt;

&lt;p&gt;You don't know until someone reports it or until you see it in your access logs and realize what happened or until a security researcher publishes it.&lt;/p&gt;

&lt;p&gt;The business cost isn't just remediation, it's the announcement you have to send to users, it's the news cycle if you're big enough, it's every enterprise customer who now has a security incident on their hands because of your tool. It's the contracts that get paused pending a security review. It's the investors who ask uncomfortable questions on the next call.&lt;/p&gt;

&lt;p&gt;A static token with no rate limiting isn't a medium-severity finding, It's a supply chain attack waiting for someone with time and motivation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stored XSS is slower and worse
&lt;/h2&gt;

&lt;p&gt;XSS via a &lt;code&gt;javascript:&lt;/code&gt; URI is dramatic, stored XSS is quieter and harder to detect.&lt;/p&gt;

&lt;p&gt;The same audit found that &lt;code&gt;release_notes&lt;/code&gt;, a markdown field returned in API responses, was stored without sanitization. Any client that renders this content as HTML executes whatever's in it.&lt;/p&gt;

&lt;p&gt;The attack vector: whoever controls the release token (or whoever gains access to the GitHub Action that uses it) can inject a payload into &lt;code&gt;release_notes&lt;/code&gt;. It sits in your database and every user who queries your version endpoint gets it back. &lt;br&gt;
If they're using a web-based dashboard, an Electron app, or any client that renders markdown to HTML, the script runs.&lt;/p&gt;

&lt;p&gt;The insidious part is that this doesn't trigger on write, it triggers on read, potentially weeks or months later, across every client simultaneously.&lt;/p&gt;

&lt;p&gt;Sanitizing on storage takes ten minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bleach&lt;/span&gt;

&lt;span class="n"&gt;ALLOWED_TAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bleach&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sanitizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALLOWED_TAGS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pre&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sanitize_release_notes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bleach&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ALLOWED_TAGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gap between "ten minutes to fix" and "six months of exposure" is that nobody thought to add it during the initial implementation. That gap is where startups get hurt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real question is what you're not seeing
&lt;/h2&gt;

&lt;p&gt;These findings came from auditing one PR on one endpoint. A Tool version management route, not the authentication system, not the payment flow, not the data pipeline. &lt;strong&gt;One endpoint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our experience analyzing production codebases, the ratio holds pretty consistently: for every vulnerability someone finds during a focused review, there are several more in the surrounding code that nobody was looking at. Not because the team is negligent, but because security review requires a different mode of reading than code quality review.&lt;/p&gt;

&lt;p&gt;A senior engineer reading this PR would catch the missing Literal types, probably flag the str on url as sloppy. But they'd need to be specifically thinking "what can an attacker do with this?" to trace the full exploit chain from a malicious URL in the database to code execution on user machines.&lt;/p&gt;

&lt;p&gt;That's not a criticism of how developers read code, it's just a different skill set, applied at a different moment. Tools like &lt;a href="https://ixtli.app" rel="noopener noreferrer"&gt;Ixtl&lt;/a&gt; exist specifically to run that analysis at PR time before the code ships, while it's still cheap to fix, without requiring a dedicated security engineer on the team.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually takes to close a startup
&lt;/h2&gt;

&lt;p&gt;You don't need a nation-state attacker, You don't need a zero-day, You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One leaked GitHub Actions secret&lt;/li&gt;
&lt;li&gt;One endpoint with no rate limiting&lt;/li&gt;
&lt;li&gt;One Tool that auto-updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And you can push a malicious binary to every user who runs your tool.&lt;/p&gt;

&lt;p&gt;For a startup with enterprise customers, that's not a security incident, it's an existential one. One customer's security team reports it, the others hear about it, You spend the next quarter doing damage control instead of building product.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Attack Vector&lt;/th&gt;
&lt;th&gt;Business Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unvalidated url field&lt;/td&gt;
&lt;td&gt;XSS / SSRF&lt;/td&gt;
&lt;td&gt;User machines compromised via auto-update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored XSS in &lt;code&gt;release_notes&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Payload delivery at scale&lt;/td&gt;
&lt;td&gt;All clients exposed simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No rate limiting on token auth&lt;/td&gt;
&lt;td&gt;Brute-force → full access&lt;/td&gt;
&lt;td&gt;Supply chain compromise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static token, no rotation&lt;/td&gt;
&lt;td&gt;Indefinite exposure post-leak&lt;/td&gt;
&lt;td&gt;No recovery path without redeployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The conversation about why startups fail is almost entirely about product, market fit, and runway. Security failures don't show up in postmortems because most companies don't publish postmortems when they get breached. &lt;br&gt;
They just quietly disappear, or spend years rebuilding trust they didn't know they were losing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do before you ship
&lt;/h2&gt;

&lt;p&gt;None of this requires a security team, it requires asking a different question during code review: what happens if someone uses this input maliciously?&lt;/p&gt;

&lt;p&gt;For the specific issues above, the checklist is short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate URL schemes, HttpUrl in Pydantic, or a custom validator that blocks everything except https:// to known domains&lt;/li&gt;
&lt;li&gt;Sanitize any field that stores user-controlled content and gets returned in API responses&lt;/li&gt;
&lt;li&gt;Add rate limiting to any endpoint that uses token-based auth, 5 requests per minute is enough to make brute-force infeasible&lt;/li&gt;
&lt;li&gt;Rotate static secrets; use GitHub OIDC for CI/CD instead of long-lived tokens when possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are 30-minute fixes, individually. The hard part is knowing you need them before someone else figures it out first.&lt;/p&gt;

&lt;p&gt;If you want a second opinion on what's in your PRs before they merge, ixtli.app runs this kind of analysis automatically not just on the diff, but across the full dependency graph of the project. The findings above were caught before the code shipped. &lt;br&gt;
That's the only time catching them is cheap.&lt;/p&gt;

&lt;p&gt;--&lt;br&gt;
&lt;em&gt;If this was useful, share it with the engineer on your team who reviews PRs. Not because they're doing something wrong but because this is the kind of thing that's easy to miss and expensive to learn the hard way.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to write a good pull request description</title>
      <dc:creator>Fernando Gallardo</dc:creator>
      <pubDate>Thu, 18 Jun 2026 16:00:00 +0000</pubDate>
      <link>https://dev.to/jfgg/how-to-write-a-good-pull-request-description-3oc6</link>
      <guid>https://dev.to/jfgg/how-to-write-a-good-pull-request-description-3oc6</guid>
      <description>&lt;p&gt;A pull request without a description is a puzzle with missing pieces.&lt;/p&gt;

&lt;p&gt;The reviewer has to reconstruct what you were trying to do, why you did it this way, and what could go wrong, from the code alone. That’s not a review, it’s archaeology. And it’s why so many PRs get approved with a “LGTM” that doesn’t mean anything.&lt;/p&gt;

&lt;p&gt;A good PR description isn’t bureaucracy. It’s the thing that makes your change reviewable — and therefore mergeable — without three rounds of back-and-forth.&lt;/p&gt;

&lt;p&gt;Why most PR descriptions fail&lt;/p&gt;

&lt;p&gt;The most common PR descriptions look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;”Fix bug”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;”Update user service”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;”WIP”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;”Changes per feedback”&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tell the reviewer nothing. They don’t explain what the bug was, what broke, what the fix does, or how to verify it works. The reviewer is starting from zero.&lt;/p&gt;

&lt;p&gt;The second most common failure is the opposite: a description that’s just a list of  every file changed, copy-pasted from the diff. That’s also useless, it describes what changed, not why.&lt;/p&gt;

&lt;p&gt;A good description answers the questions a reviewer would ask if they could.&lt;/p&gt;

&lt;p&gt;The goal is to make the review faster and the approval more confident.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to include in a pull request
&lt;/h2&gt;

&lt;p&gt;A PR description that actually helps has four components. None of them are long, a good description for a focused PR can be under 150 words. The point is structure, not volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What this change does (2-4 sentences)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a list of files. Not a commit log. A plain explanation of what problem this PR solves and what it does to solve it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This PR adds rate limiting to the &lt;code&gt;/auth/login&lt;/code&gt; endpoint. Witout it, the endpoint can be used for credential stuffing with no hrottling. The implementation uses a sliding window counter in Redis with a 60-second TTL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;2. Why this approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If there were alternative implementations you considered and rejected, say so.&lt;/p&gt;

&lt;p&gt;This prevents reviewers from suggesting alternatives you already evaluated, and it shows your reasoning.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We considered using an in-memory counter, but that doesn’t work across multiple instances. Redis was already in the stack, so the overhead is minimal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. How to test it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Specific steps. Not “run the tests”, which tests? Where? Under what conditions?&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Start the service locally with &lt;code&gt;docker compose up&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hit &lt;code&gt;/auth/login&lt;/code&gt; more than 10 times in 60 seconds with any credentials&lt;/li&gt;
&lt;li&gt;The 11th request should return 429&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;4. What’s out of scope&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one gets skipped most often, but it prevents the review from expanding into territory you didn’t intend to cover.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This PR doesn’t address rate limiting on other endpoints. that’s tracked in #412.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pull request best practices for developers
&lt;/h2&gt;

&lt;p&gt;Beyond the description itself, there are a few pull request best practices that  consistently improve review quality:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep PRs focused.&lt;/strong&gt; A PR that does one thing is easier to review than one that does three. If you’re fixing a bug and refactoring a module and updating a dependency, that’s three PRs, or at minimum, a description that clearly separates the three concerns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Link to the issue or ticket.&lt;/strong&gt; The PR description doesn’t need to contain all the context,  it needs to point to where the context lives. A link to the relevant issue, Jira ticket, or design doc is worth more than a paragraph trying to summarize it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t self-review your own PR right after writing it.&lt;/strong&gt; The gap between writing code and reviewing it needs to be longer than five minutes. Come back to it after a break, or ask for a review first and do your own pass after seeing the comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a screenshot or recording for UI changes.&lt;/strong&gt; A reviewer who can see the before/after state will spot visual regressions that are invisible in a diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security section most teams skip
&lt;/h2&gt;

&lt;p&gt;There’s one section most PR templates don’t include: a note on security implications.&lt;/p&gt;

&lt;p&gt;Not every PR needs this, a typo fix doesn’t. But any PR that touches authentication, authorization, input handling, file uploads, external APIs, or new dependencies  should include a line or two about what the security surface is and how it’s handled.&lt;/p&gt;

&lt;p&gt;This doesn’t need to be a formal threat model. It can be as simple as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Security: This endpoint accepts file uploads. Files are validated for MIME type and size limit before being written to S3. No user-supplied file path is used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dependencies: Added &lt;a href="mailto:jsonwebtoken@9.0.2"&gt;jsonwebtoken@9.0.2&lt;/a&gt;. Checked against OSV, no known  CVEs. Replaces the unmaintained jwt-simple package.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Teams that use automated security analysis in CI can use that output as a reference, if the scanner flagged something, the PR description should acknowledge it and explain how it was resolved. Platforms like Ixtli produce per-PR security summaries that make this easy to include without doing the research manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  A template you can actually use
&lt;/h2&gt;

&lt;p&gt;Here’s a minimal template that covers the essentials. Adapt it to your team’s conventions, the point isn’t to fill out every field for every PR, it’s to make the important fields habitual.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## What&lt;/span&gt;

[1-3 sentences: what problem does this solve and how]

&lt;span class="gu"&gt;## Why this approach&lt;/span&gt;

[Optional: alternatives considered and why they were rejected]

&lt;span class="gu"&gt;## How to test&lt;/span&gt;

[Specific steps to verify the change works]

&lt;span class="gu"&gt;## Out of scope&lt;/span&gt;

[What this PR explicitly doesn't cover]

&lt;span class="gu"&gt;## Security notes&lt;/span&gt;

[Optional: relevant security surface, how it's handled, or "no security impact — [reason]"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep it in a .github/pull_request_template.md file and GitHub will auto-populate it for every PR.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitLab
&lt;/h3&gt;

&lt;p&gt;Keep it in a .gitlab/merge_request_templates/Default.md&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-project/
├── .gitlab/
│   └── merge_request_templates/
│       └── Default.md
├── src/
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## What&lt;/span&gt;

Describe the change and the problem it solves.

&lt;span class="gu"&gt;## Why&lt;/span&gt;

Why was this implementation chosen?

&lt;span class="gu"&gt;## How to test&lt;/span&gt;

Steps to verify the change works.

&lt;span class="gu"&gt;## Risks&lt;/span&gt;

Potential side effects or areas that may be impacted.

&lt;span class="gu"&gt;## Security impact&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] No security impact
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Authentication / Authorization
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Input validation
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Secrets / Credentials
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Data exposure
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Infrastructure / Configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;A PR description is not documentation for posterity. It’s communication for the reviewer who has to understand your change in the next 20 minutes.&lt;/p&gt;

&lt;p&gt;The investment is small — 10 to 15 minutes to write a description that gives your reviewer everything they need. The return is a review that’s faster, more accurate, and less likely to require three rounds of clarification.&lt;/p&gt;

&lt;p&gt;If your team’s PRs consistently lack context, the easiest fix is a template in the repo. Add it this week. If you want to take the security side further&lt;/p&gt;

&lt;p&gt;without making it a manual process, &lt;a href="https://ixtli.app" rel="noopener noreferrer"&gt;ixtli.app&lt;/a&gt; integrates directly into the PR workflow.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>Cómo hacer una buena revisión de código</title>
      <dc:creator>Fernando Gallardo</dc:creator>
      <pubDate>Thu, 18 Jun 2026 03:08:01 +0000</pubDate>
      <link>https://dev.to/jfgg/como-hacer-una-buena-revision-de-codigo-5db8</link>
      <guid>https://dev.to/jfgg/como-hacer-una-buena-revision-de-codigo-5db8</guid>
      <description>&lt;p&gt;Revisar código es una de las actividades más subestimadas del desarrollo de software.&lt;/p&gt;

&lt;p&gt;La mayoría de los equipos la tratan como un trámite, algo que hay que aprobar antes de mergear. El resultado es que los PRs se aprueban con un “LGTM” después de dos minutos de scroll, y los problemas reales pasan de largo.&lt;/p&gt;

&lt;p&gt;Una revisión bien hecha no es leer línea por línea buscando typos. Es entender qué intenta hacer ese código, si lo hace de la manera correcta, y si introduce riesgos que no existían antes. Eso requiere un proceso, no un instinto.&lt;/p&gt;

&lt;p&gt;Este artículo cubre cómo estructurar ese proceso: qué revisar, en qué orden, qué preguntas hacer, y cómo detectar problemas de seguridad sin ser un experto en ciberseguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Empieza por el contexto, no por el código
&lt;/h2&gt;

&lt;p&gt;El error más común en code review es abrir el diff y empezar a leer desde la primera línea modificada. Antes de ver una sola línea, necesitas entender qué problema resuelve este cambio.&lt;/p&gt;

&lt;p&gt;Lee la descripción del PR. Si no hay descripción, o si dice “fixes bug”, ya encontraste  el primer problema. Un PR sin contexto obliga al reviewer a reconstruir el  razonamiento del autor desde cero, y eso aumenta la probabilidad de aprobar algo que no debería aprobarse.&lt;/p&gt;

&lt;p&gt;Lo que un buen PR debe explicar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Qué cambia no cómo, sino qué problema resuelve&lt;/li&gt;
&lt;li&gt;Por qué este approach si hay alternativas que se descartaron, decirlo&lt;/li&gt;
&lt;li&gt;Cómo probarlo, pasos para verificar que funciona&lt;/li&gt;
&lt;li&gt;Qué no cubre, scope explícito evita confusiones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si tienes esa información antes de ver el diff, tu revisión va a ser significativamente más efectiva.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué revisar y en qué orden
&lt;/h2&gt;

&lt;p&gt;No toda línea de código merece el mismo nivel de atención. Un buen reviewer distribuye su energía de forma inteligente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primero: arquitectura y flujo de datos.&lt;/strong&gt; ¿El cambio tiene sentido a nivel de diseño? ¿Agrega una dependencia innecesaria? ¿Rompe alguna abstracción existente? Esto es lo más difícil de cambiar después.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Segundo: lógica de negocio.&lt;/strong&gt; ¿El código hace lo que dice que hace? ¿Los edge cases están cubiertos? ¿Qué pasa si el input viene vacío, nulo, o malformado?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tercero: seguridad.&lt;/strong&gt; Este punto merece su propia sección.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cuarto: legibilidad y convenciones.&lt;/strong&gt; Nombres de variables, estructura de funciones, comentarios. Importante, pero no el punto de partida.&lt;/p&gt;

&lt;p&gt;La mayoría de los reviewers hacen esto al revés, empiezan por los detalles de estilo y llegan agotados a lo que realmente importa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo detectar problemas de seguridad en code review
&lt;/h2&gt;

&lt;p&gt;Detectar vulnerabilidades durante un code review no requiere ser un especialista en seguridad. Requiere hacerse las preguntas correctas.&lt;/p&gt;

&lt;p&gt;Hay patrones que aparecen consistentemente en PRs problemáticos:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entradas sin validar.&lt;/strong&gt; ¿Los datos que vienen del exterior (usuario, API, archivo) se usan directamente sin sanitizar? Cualquier lugar donde un string externo toca una query, un path de archivo, o una llamada a sistema es candidato a revisión.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manejo de errores que expone información.&lt;/strong&gt; Stack traces devueltos al cliente, mensajes de error con rutas internas, logs que incluyen tokens o passwords. Esto pasa más de lo que parece.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencias nuevas.&lt;/strong&gt; Cada &lt;code&gt;npm install&lt;/code&gt; o &lt;code&gt;pip install&lt;/code&gt; que aparec en el diff es una superficie de ataque potencial. ¿Se revisó el paquete? ¿Tiene CVE conocidos? ¿Cuántos mantenedores activos tiene?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets hardcodeados.&lt;/strong&gt; Claves API, tokens, URLs de bases de datos. Son más comunes de lo que cualquier equipo quiere admitir.&lt;/p&gt;

&lt;p&gt;Algunos equipos integran análisis automatizado directamente en el flujo de revisión para que estos patrones se marquen antes de que el reviewer los vea. Herramientas como &lt;a href="https://ixtli.app" rel="noopener noreferrer"&gt;Ixtli&lt;/a&gt;, por ejemplo, analizan el diff completo del PR contra el grafo de dependencias del proyecto, no solo las líneas cambiadas, lo que hace que ciertos tipos de vulnerabilidades sean mucho más difíciles de pasar por alto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo dar feedback que sirva
&lt;/h2&gt;

&lt;p&gt;El objetivo de un comentario en un code review no es demostrar que encontraste algo malo. Es que el código mejore.&lt;/p&gt;

&lt;p&gt;Algunas reglas que hacen una diferencia real:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sé específico.&lt;/strong&gt; “Este código es confuso” no ayuda. “Esta función hace tres cosas distintas, considera extraer la lógica de validación a una función separada” sí ayuda.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distingue entre bloqueante y sugerencia.&lt;/strong&gt; No todo comentario debe impedir el merge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefija&lt;/strong&gt; con [bloqueante], [sugerencia] o [nitpick] para que el autor sepa qué es crítico y qué es opcional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pregunta antes de asumir.&lt;/strong&gt; Si algo no se entiende, pregunta. “¿Por qué se usa &lt;code&gt;setTimeout&lt;/code&gt; aquí en lugar de un event listener?” es mejor que asumir que es un error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aprueba cuando está listo.&lt;/strong&gt; No sigas agregando comentarios menores una vez que los problemas reales están resueltos. El proceso de review también tiene un costo.&lt;/p&gt;

&lt;p&gt;Checklist para code review&lt;/p&gt;

&lt;p&gt;Antes de aprobar un PR, recorre estos puntos:&lt;/p&gt;

&lt;p&gt;| Área | Pregunta |&lt;/p&gt;

&lt;p&gt;|------|---------|&lt;/p&gt;

&lt;p&gt;| Contexto | ¿El PR tiene descripción clara de qué resuelve? |&lt;/p&gt;

&lt;p&gt;| Diseño | ¿El cambio tiene sentido arquitectónico? |&lt;/p&gt;

&lt;p&gt;| Lógica | ¿Los edge cases están cubiertos? |&lt;/p&gt;

&lt;p&gt;| Seguridad | ¿Hay entradas sin validar, secretos expuestos, o dependencias nuevas sin revisar? |&lt;/p&gt;

&lt;p&gt;| Tests | ¿Los tests cubren los casos relevantes, no solo el happy path? |&lt;/p&gt;

&lt;p&gt;| Deuda técnica | ¿El cambio deja el código en mejor estado del que lo encontró? |&lt;/p&gt;

&lt;p&gt;No es una lista para leer rápido. Es una lista para detenerse en cada punto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Una buena revisión de código no es más lenta que una mala, es más intencional.&lt;/p&gt;

&lt;p&gt;La diferencia está en saber qué buscar y en qué orden.&lt;/p&gt;

&lt;p&gt;Si tu equipo trata los PRs como trámites, el problema no es el código,  es el proceso.&lt;/p&gt;

&lt;p&gt;Empezar a usar un checklist consistente, pedir descripciones reales en cada PR, y separar los comentarios bloqueantes de las sugerencias son cambios que se pueden implementar esta semana sin herramientas adicionales.&lt;/p&gt;

&lt;p&gt;Para la parte de seguridad, revisar manualmente cada dependencia nueva o cada input sin validar es costoso en tiempo. Vale la pena evaluar qué parte de ese trabajo puede automatizarse, ixtli.app está construido específicamente para ese slice del proceso.&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>development</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
