<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator>
  <link href="https://blog.lesis.lat/feed/en.xml" rel="self" type="application/atom+xml" />
  <link href="https://blog.lesis.lat/en/" rel="alternate" type="text/html" />
  <updated>2026-10-11T17:55:09-03:00</updated>
  <id>https://blog.lesis.lat/feed/en.xml</id>
  <title type="html">LESIS - Laboratory of Engineering Studies in Information Security</title>
  <subtitle>Applied offensive security research by LESIS: vulnerability research, exploitation techniques, custom tooling and practical guides.</subtitle>
  <author>
    <name>LESIS</name>
  </author>
  <entry xml:lang="en">
    <title type="html">Never the same IP twice: building cf-proxy on Cloudflare Workers</title>
    <link href="https://blog.lesis.lat/blog/cf-proxy-ip-rotation-with-cloudflare-workers/" rel="alternate" type="text/html" title="Never the same IP twice: building cf-proxy on Cloudflare Workers" />
    <link href="https://blog.lesis.lat/assets/audio/cf-proxy-ip-rotation/en.mp3" rel="enclosure" type="audio/mpeg" length="5103101" />
    <published>2026-10-11T09:00:00-03:00</published>
    <updated>2026-10-11T09:00:00-03:00</updated>
    <id>https://blog.lesis.lat/blog/never-the-same-ip-twice-cloudflare-workers-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/blog/cf-proxy-ip-rotation-with-cloudflare-workers/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;During a pentest it is often the case that you have to change your IP address momentarily for some reason. Maybe you are trying to perform successive requests to an endpoint with an IP-based rate limit, maybe your current IP has been blocked by a firewall as a result of a previous test (or just because your provider has a bad reputation). Whatever the reason may be, you probably had to connect to a VPN or proxy at some point in your life.&lt;/p&gt;

&lt;p&gt;But when this happens during a security assessment, the problem usually doesn’t stop there. If you were blocked once it is very likely that you will be blocked again soon, and you will need a way to change your address yet again. Eventually this becomes very annoying, especially if your target is banning your IP address too often and you are performing a time-sensitive test. In this situation the best option would be to automate the rotation of IP addresses so that no two consecutive requests come from the same origin.&lt;/p&gt;

&lt;p&gt;There are some services that provide this functionality, but finding a good one is hard. Most of them are intended for crawlers and data scraping companies and are high-priced for providing IP addresses with good reputation to avoid being flagged as automated traffic. The cheaper ones will hand you addresses from a pool of cloud providers (AWS, GCP, Oracle, etc.) which are very likely to be detected as suspicious or automated, while the services that do provide residential proxies with addresses from regular ISPs can be very shady (with some even selling illegal services fuelled by botnets).&lt;/p&gt;

&lt;p&gt;In this scenario, a tool like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; can be a game changer. For one, a Workers plan is pretty cheap ($5.00/month will get you 10 million requests) and there is little setup needed. You just configure the proxy options for whatever client you are using and every connection you make will have a different address almost every time, so you don’t need to worry about having multiple proxies to round-robin between. And reputation-wise, a Cloudflare IP address is not too bad, as they are often trusted and even whitelisted. They say that 1 in every 5 sites on the internet use their services, so blocking their ASNs can be very dangerous to some cloud providers.&lt;/p&gt;

&lt;p&gt;That said, using a worker as a proxy is not without its limitations. The biggest one is that Cloudflare doesn’t allow outbound TCP connections to its own IP address ranges, so you will have trouble connecting to sites that are behind their reverse proxy (which many sites are). They also refuse to connect to port 25/TCP (usually dedicated to SMTP) of any target, but that’s usually not that big of an issue.&lt;/p&gt;

&lt;h2 id=&quot;what-cf-proxy-is-and-how-it-works&quot;&gt;What cf-proxy is, and how it works&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; is a utility that allows routing your traffic through Cloudflare’s global network of servers, masking the origin IP address and achieving automatic IP rotation in the process. Because Cloudflare has become so popular and widely adopted, their ASNs tend to be trusted and even whitelisted in some firewalls, especially when it is being used as a reverse proxy to leverage their WAF solution while keeping the origin server of a web application hidden. In these scenarios, this utility can also be helpful to bypass these network filters and talk to the origin server directly.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/cf-proxy/demo-cf.webp&quot; alt=&quot;cf-proxy routing requests through Cloudflare Workers, with the source IP changing between requests&quot; width=&quot;1680&quot; height=&quot;913&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At its heart, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; is not too complicated. The serverless architecture on which it runs does most of the work. In fact, the whole codebase consists of only two files, and the &lt;a href=&quot;https://github.com/LvMalware/cf-proxy/blob/master/worker/src/index.js&quot;&gt;code&lt;/a&gt; that is deployed to Cloudflare Workers is just 35 lines of JavaScript:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cloudflare:sockets&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TOKEN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;lt;YOUR-AUTH-TOKEN&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TOKEN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Unauthorized&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;upgradeHeader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Upgrade&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;upgradeHeader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;upgradeHeader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;websocket&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Expected Upgrade: websocket&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;426&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;X-Proxy-Target&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;writer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;websocket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WebSocketPair&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;websocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arrayBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;writer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

            &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipeTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WritableStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}));&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;webSocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is all it takes to forward your traffic. The worker receives a request, authenticates it using a token in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header (after all, we don’t want anyone using our workers to do bad things, right?), checks if this is a valid WebSocket upgrade request, connects to the target received via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Proxy-Target&lt;/code&gt; header, upgrades the connection to the WebSocket protocol and then proceeds to stream any data it receives, transferring it between the proxy client and the target server. The whole IP rotation thing is just a side-effect of how the worker instances are spawned to handle requests.&lt;/p&gt;

&lt;p&gt;The workers are deployed across Cloudflare’s global network of servers and are provisioned on-demand when they are invoked. There is some smart provisioning algorithm&lt;sup id=&quot;fnref:provisioning&quot;&gt;&lt;a href=&quot;#fn:provisioning&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; going on to minimize latency and provisioning time, but because this is a very fast process and the network is just huge, it is very unlikely that two subsequent invocations will be deployed within the same server, hence the address will almost certainly differ on the next invocation.&lt;/p&gt;

&lt;p&gt;On the other side of this process, we have the &lt;a href=&quot;https://github.com/LvMalware/cf-proxy/blob/master/proxy.js&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy.js&lt;/code&gt;&lt;/a&gt; file which runs locally and provides the proxy server (either HTTP or SOCKS5) that our client will use when talking to the internet. Although a little bigger (around 230 lines), it is just as simple as the previous one. The most complicated part is probably the state machine used to provide the SOCKS5 server. All this script does is parse command-line options (in a not very optimal way) to identify the worker, auth token and the proxy protocol it should provide, then starts accepting connections on a TCP port. For each received connection it identifies the target based on the proxy protocol in use and initiates the WebSocket connection to the specified worker, passing the authentication token and target host via HTTP headers as previously mentioned. And if this works, the data is transferred between the client and the worker until the connection closes.&lt;/p&gt;

&lt;h2 id=&quot;the-origin-story&quot;&gt;The origin story&lt;/h2&gt;

&lt;p&gt;The foundation of what would later become &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; was born from a real need during a pentest.&lt;/p&gt;

&lt;p&gt;I needed to bypass an IP-based rate limit for a specific enumeration task. From what I could tell, the target application enforced a limit of 5 reqs/min with increasing backoff for repeated offenses. I could get around this by changing my IP address every 5 requests and only reusing the same IP after a minute. The cheap way to do that was to deploy some computing instances in a cloud provider with SSH access and then tunnel my requests through them in a round-robin fashion, but that would require 12 instances just to perform 1 request per second. That’s doable, but very slow for this scenario. I had estimated 100k requests to exhaust the search space, which would take more than 24 hours to complete at a rate of 1 rps. It was clear that I needed a better approach, but I didn’t feel like purchasing a commercial rotating proxy for that (for the reasons previously mentioned).&lt;/p&gt;

&lt;p&gt;While looking for alternatives I found an article about using AWS Lambda functions as proxies. The problem was that, due to the way the lambdas are provisioned, the IP address would not change for 5-15 minutes and this would not work for my specific use case, but it gave me the idea of using serverless applications as proxies. A work colleague had recently introduced me to Cloudflare Workers and I found them very interesting, but as someone that doesn’t do web development I hadn’t found a good use for them yet, so it seemed like a good opportunity to try.&lt;/p&gt;

&lt;p&gt;To validate the idea, I created a free Cloudflare account and deployed a worker using example code from the &lt;a href=&quot;https://developers.cloudflare.com/workers/examples/fetch-html/&quot;&gt;official documentation&lt;/a&gt; that would request a remote API to return the client’s (the worker itself, in this case) IP address, then I tested by invoking the worker multiple times and checking that the IP address was different on every request. It worked like magic!&lt;/p&gt;

&lt;p&gt;Building on this principle, I rewrote the worker to receive the required parameters and make the requests to the target application for me, returning whatever result it got back. This proved to be a viable solution, allowing me to finish the enumeration way faster without being blocked, and the free plan almost covered all the requests I needed. Although the core functionality of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; was already present in this initial version, it was very limited and I didn’t touch it again for some time after.&lt;/p&gt;

&lt;p&gt;During another security assessment a few days later, I had identified a possible injection vulnerability on a target through code review, but any attempts to exploit it were being blocked by Cloudflare. Unable to craft a payload to bypass the filter, I had to get around the WAF somehow.&lt;/p&gt;

&lt;p&gt;Looking at various sources, I found that the domain hosting the target application used to resolve to a specific IP address up until some point in the past, after which it started to point to Cloudflare. Because the same IP seemed to have been hosting that application for a long time before, it seemed reasonable that they hadn’t actually changed the host, but rather just placed the WAF in front of it. But even though the host seemed to still be alive, it didn’t accept connections on any of the ports I tried. My assumption was that they had put a firewall rule in place to only accept traffic from Cloudflare’s ASN, so people wouldn’t be able to talk to the web server directly (like I was trying to do).&lt;/p&gt;

&lt;p&gt;If only I could make it look like my traffic was coming from Cloudflare… wait… I can!&lt;/p&gt;

&lt;p&gt;To test if this was the case I deployed a worker that would make requests to that IP address on ports 80 and 443, which confirmed there was something responding on port 80. Now I had a way to bypass the WAF by using a worker to make the requests directly to the origin server on my behalf, so I just modified the worker to receive some parameters to define things like request method, path, body, and so on.&lt;/p&gt;

&lt;p&gt;At this point I could already proceed with the assessment, but it became clear to me that this small hack could have real applications in many other scenarios, so I decided to turn it into a tool.&lt;/p&gt;

&lt;p&gt;Later I improved the worker to behave like a real proxy, making connections to remote hosts and forwarding the traffic. Using WebSockets was the natural choice, since they allow streaming data between both sides of the connection more easily. The first version only provided an HTTP proxy, then I added SOCKS5 support a few days later. And that’s how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; came to be.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;IP rotation during an assessment doesn’t have to mean paying for a shady residential proxy or juggling a dozen cloud instances. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf-proxy&lt;/code&gt; turns Cloudflare Workers into a cheap rotating proxy that hands you a fresh, generally trusted address on almost every request, and the same trick that hides your origin can also take you past a WAF and straight to the server behind it. It isn’t a silver bullet, as you can’t reach hosts inside Cloudflare’s own ranges and port 25 is off the table, but for the cases it does cover it is hard to beat on price and setup. The code is on &lt;a href=&quot;https://github.com/LvMalware/cf-proxy&quot;&gt;GitHub&lt;/a&gt; if you want to try it.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:provisioning&quot;&gt;
      &lt;p&gt;Workers are provisioned in locations that are physically close to the origin of the request to minimize latency, so they are often in the same global region (or at least the same country) as your own origin IP. &lt;a href=&quot;#fnref:provisioning&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content>
    <author>
      <name>Lucas Vieira Araujo</name>
    </author>
    <category term="Research" />
    <summary type="html">How cf-proxy turns Cloudflare Workers into a cheap rotating proxy, with a different source IP on almost every request and a trusted ASN, and the pentests that led to building it.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Our first observation of an AI-assisted malicious group targeting small and medium-sized Brazilian fintech companies</title>
    <link href="https://blog.lesis.lat/blog/our-first-observation-of-an-ai-assisted-malicious-group/" rel="alternate" type="text/html" title="Our first observation of an AI-assisted malicious group targeting small and medium-sized Brazilian fintech companies" />
    <link href="https://blog.lesis.lat/assets/audio/ai-assisted-malicious-group/en.mp3" rel="enclosure" type="audio/mpeg" length="3760868" />
    <published>2026-06-29T15:00:00-03:00</published>
    <updated>2026-06-29T15:00:00-03:00</updated>
    <id>https://blog.lesis.lat/blog/our-first-observation-of-an-ai-assisted-malicious-group-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/blog/our-first-observation-of-an-ai-assisted-malicious-group/">&lt;p&gt;LESIS’ work often places us across different fronts of offensive security, incident response, and threat intelligence. On a recent occasion, we were called in to support an investigation involving a Brazilian financial institution. Our initial role was to answer an objective question: what entry vector was used by the malicious actor?&lt;/p&gt;

&lt;p&gt;To do that, we conducted an analysis guided by adversary simulation. We started from the same operational premise as the attacker: compromise an application or adjacent resource that could, directly or indirectly, lead to systems with potential financial movement. This approach allowed us to reconstruct part of the attack chain, identify the initial vector, and later locate persistence mechanisms used by the threat actor.&lt;/p&gt;

&lt;p&gt;During the investigation, we observed that the persistence deployed by the group communicated with external infrastructure controlled by the operators. From that point, we deepened our analysis of the command-and-control infrastructure, aiming to understand whether there were additional elements that could support the incident response and help protect other potential targets.&lt;/p&gt;

&lt;p&gt;For operational reasons, we will not publish IoCs, payloads, addresses, file names, internal paths, or details that could compromise investigations. Even so, the analyzed artifacts revealed a consistent set of behaviors, tools, and operational patterns.&lt;/p&gt;

&lt;h2 id=&quot;what-was-observed&quot;&gt;What was observed&lt;/h2&gt;

&lt;p&gt;The analyzed infrastructure contained payloads, exploitation support artifacts, request logs, data-receiving scripts, and evidence of previous attempts against multiple targets. In some cases, the same infrastructure held information related to more than one target or operation, including failed attempts, ongoing activities, and data obtained after compromise.&lt;/p&gt;

&lt;p&gt;The analysis of these materials led us to six main conclusions.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The observed targets were Brazilian fintechs, especially small and medium-sized organizations, with lower media exposure and lower publicly visible security maturity when compared to large financial institutions.&lt;/li&gt;
  &lt;li&gt;The evidence indicates the activity of a group, not an isolated operator. This assessment is based on multiple signals: different access identifiers, concurrent activity from distinct origins, operational patterns incompatible with a single continuous human session, and stylometric differences in code artifacts.&lt;/li&gt;
  &lt;li&gt;At least one of the operators appears to be familiar with Brazilian Portuguese. This conclusion comes from the use of the language in operational artifacts and comments observed during the analysis.&lt;/li&gt;
  &lt;li&gt;We identified evidence of LLM use to support the construction of offensive tooling. The artifacts indicate the use of language models to assist in creating or adapting exploits, web shells, bypass mechanisms, exfiltration scripts, and auxiliary components used during the operation.&lt;/li&gt;
  &lt;li&gt;We observed attempts to bypass LLM guardrails through misleading contextualization, especially by leading the model to operate under the premise of an authorized pentest. This pattern is relevant because it shows that the group’s use of AI was not limited to productivity: it was also incorporated into the offensive development process.&lt;/li&gt;
  &lt;li&gt;One relevant point observed was the duration of one of the operations associated with this set of artifacts. In one target, we identified activity distributed over approximately three months, indicating a direct and continuous intent against the compromised organization. This behavior reduces the likelihood of an isolated opportunistic attempt and reinforces the assessment that the group maintained operational interest in the target, adapting its approach as it encountered barriers, new access opportunities, and potential paths to increase impact.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;attack-chain&quot;&gt;Attack chain&lt;/h2&gt;

&lt;p&gt;The common vector observed was the exploitation of application-level vulnerabilities. The group did not appear to rely exclusively on applications directly connected to financial flows. On the contrary: it searched for applications in the same infrastructure or in adjacent environments that could serve as an entry point for lateral movement.&lt;/p&gt;

&lt;p&gt;After initial access, the group searched for credentials, configuration files, environment variables, internal integrations, and administration resources that could expand access.&lt;/p&gt;

&lt;p&gt;In cases where the compromise progressed, persistence was simple. In the analyzed artifacts, we did not observe a sophisticated effort to hide. The goal appeared to be functionality and operational speed, not advanced stealth. This did not reduce the potential impact of the operation: even simple implants can be enough when combined with valid credentials, weak segmentation, excessive permissions, and low visibility into egress traffic.&lt;/p&gt;

&lt;p&gt;In at least one case, existing controls prevented direct communication between the implant and the external infrastructure. The group’s response was to adapt the operation. We observed attempts to deliver blind payloads, probing of different ports, and successive adjustments to understand the applied control and decide how to bypass it. This behavior indicates operational capability and persistence, even though the tooling itself was not particularly sophisticated.&lt;/p&gt;

&lt;p&gt;We also observed a focus on credential exfiltration. The group sought access to other applications and services used by employees of the compromised organization, suggesting an interest in scaling the attack beyond the first exploited system.&lt;/p&gt;

&lt;h2 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h2&gt;

&lt;p&gt;There are three points that make this campaign relevant:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The first is the choice of targets: small and medium-sized Brazilian fintechs may operate relevant financial assets, critical integrations, and sensitive transactional flows, but they do not always have the same detection, response, and hardening capacity found in large financial institutions. This creates an attractive surface for financially motivated groups.&lt;/li&gt;
  &lt;li&gt;The second is the role of applications as an entry point: the operation reinforces a recurring pattern in many organizations: the path to financial impact does not necessarily begin in the main financial system. It may begin in a legacy application, an administrative service, a poorly monitored integration, or an internal component that shares infrastructure with more critical systems.&lt;/li&gt;
  &lt;li&gt;The third is the use of AI as an accelerator: the use of LLMs does not automatically turn an ordinary operator into an advanced actor, but it reduces cost, accelerates iteration, and facilitates tooling adaptation. In practice, this allows groups with intermediate capability to produce more payload variations, test hypotheses faster, and overcome obstacles with less dependence on deep specialized knowledge.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;observed-ttps--mitre-attck&quot;&gt;Observed TTPs — MITRE ATT&amp;amp;CK&lt;/h2&gt;

&lt;p&gt;Behavioral description of the group’s TTPs. It does not include IoCs, such as IPs, hashes, file names, internal paths, or victim names.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Tactic&lt;/th&gt;
      &lt;th&gt;Technique (ATT&amp;amp;CK)&lt;/th&gt;
      &lt;th&gt;Observed behavior&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Resource Development (TA0042)&lt;/td&gt;
      &lt;td&gt;Obtain Capabilities: Tool (T1588.002)&lt;/td&gt;
      &lt;td&gt;Use of ready-made public exploits together with custom scripts (AI-assisted / agentic)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Resource Development (TA0042)&lt;/td&gt;
      &lt;td&gt;Develop Capabilities (T1587)&lt;/td&gt;
      &lt;td&gt;Generation of tooling, playbooks, and reports assisted by AI / agentic systems&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Initial Access (TA0001)&lt;/td&gt;
      &lt;td&gt;Exploit Public-Facing Application (T1190)&lt;/td&gt;
      &lt;td&gt;Application-level exploitation against exposed services, including legacy and modern applications&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Discovery (TA0007)&lt;/td&gt;
      &lt;td&gt;Network Service Discovery (T1046)&lt;/td&gt;
      &lt;td&gt;Internal network reconnaissance, from the obtained access, against adjacent services&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Execution / Lateral Movement (TA0002 / TA0008)&lt;/td&gt;
      &lt;td&gt;Server-side abuse → administration console (T1190 → T1505.003)&lt;/td&gt;
      &lt;td&gt;Pivot to administration/deployment console in order to implant code&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Persistence (TA0003)&lt;/td&gt;
      &lt;td&gt;Web Shell (T1505.003) + Masquerading (T1036)&lt;/td&gt;
      &lt;td&gt;Web shells disguised as legitimate endpoints of the application itself, such as health checks&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Privilege Escalation (TA0004)&lt;/td&gt;
      &lt;td&gt;Escape to Host (T1611)&lt;/td&gt;
      &lt;td&gt;Container-to-host access due to deficient container isolation&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Credential Access (TA0006)&lt;/td&gt;
      &lt;td&gt;Credentials in Files / Private Keys (T1552.001 / .004)&lt;/td&gt;
      &lt;td&gt;Collection of credentials in configuration files and keys, as well as secrets exposed in artifacts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Credential Access (TA0006)&lt;/td&gt;
      &lt;td&gt;Steal Application Access Token (T1528)&lt;/td&gt;
      &lt;td&gt;Theft of access tokens for reuse (replay)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Credential Access (TA0006)&lt;/td&gt;
      &lt;td&gt;Brute Force: Password Cracking (T1110.002)&lt;/td&gt;
      &lt;td&gt;Offline cracking of exfiltrated hashes using contextual wordlists&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Collection (TA0009)&lt;/td&gt;
      &lt;td&gt;Email Collection (T1114)&lt;/td&gt;
      &lt;td&gt;Bulk download of mailboxes and targeted searches for remote access and financial system credentials&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Defense Evasion (TA0005)&lt;/td&gt;
      &lt;td&gt;Account Manipulation (T1098)&lt;/td&gt;
      &lt;td&gt;Modification of an administrator’s password hash to a known value&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Command and Control (TA0011)&lt;/td&gt;
      &lt;td&gt;Non-Standard Port / Fallback Channels (T1571 / T1008)&lt;/td&gt;
      &lt;td&gt;When egress traffic was blocked: blind payloads, scanning of multiple ports, and iterative channel adjustment&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Impact (TA0040)&lt;/td&gt;
      &lt;td&gt;Financially motivated targeting (intent)&lt;/td&gt;
      &lt;td&gt;Focus on payment, credit, and banking data and credentials&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This investigation shows a financially motivated group with clear sector focus and the ability to adapt tooling according to the controls found in the victim’s environment. The use of AI appears as an element of operational acceleration, especially in the creation and modification of offensive tools, but it does not replace the traditional foundations of intrusion: application exploitation, credential collection, lateral movement, persistence, and the pursuit of financial impact.&lt;/p&gt;

&lt;p&gt;We will not publish IoCs or sensitive technical details at this time. The decision is intentional: to preserve intelligence advantage, protect potential victims, and prevent the observed infrastructure or methods from being quickly changed by the operators.&lt;/p&gt;

&lt;p&gt;If your organization would like access to the detailed technical report, please contact us. We are always available to collaborate with security, incident response, and threat intelligence teams.&lt;/p&gt;</content>
    <author>
      <name>LESIS Team</name>
    </author>
    <category term="Case Study" />
    <summary type="html">An investigation by LESIS has identified a malicious campaign targeting small and medium-sized Brazilian fintech companies, using AI to accelerate the development of offensive tools, adapt payloads and support the exploitation of applications.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Dependabot: automating dependency updates on GitHub</title>
    <link href="https://blog.lesis.lat/blog/dependabot-automating-dependency-updates-on-github/" rel="alternate" type="text/html" title="Dependabot: automating dependency updates on GitHub" />
    <link href="https://blog.lesis.lat/assets/audio/dependabot-updates/en.mp3" rel="enclosure" type="audio/mpeg" length="4394850" />
    <published>2026-06-25T08:00:00-03:00</published>
    <updated>2026-06-25T08:00:00-03:00</updated>
    <id>https://blog.lesis.lat/blog/dependabot-automating-dependency-updates-on-github-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/blog/dependabot-automating-dependency-updates-on-github/">&lt;p&gt;Keeping dependencies up to date is one of the most neglected tasks in the software development lifecycle. Not because of carelessness or lack of awareness, but because everyday work imposes more visible priorities: features, bug fixes, deadlines. Outdated dependencies rarely cause immediate symptoms. The problems they introduce usually appear late, and when they do, the cost is high.&lt;/p&gt;

&lt;p&gt;Based on this diagnosis, we decided to configure Dependabot in our repositories. This text describes what the tool is, how it works, how we configured it, and what we learned during the process.&lt;/p&gt;

&lt;h2 id=&quot;the-dependency-problem&quot;&gt;The dependency problem&lt;/h2&gt;

&lt;p&gt;Every modern software project depends on external libraries. These libraries, in turn, have their own dependencies. As time passes, new versions are published with bug fixes, performance improvements, and, most importantly, security vulnerability fixes.&lt;/p&gt;

&lt;p&gt;When a vulnerability is discovered in a widely used library, it may receive a CVE (Common Vulnerabilities and Exposures) identifier and be registered in public vulnerability databases and advisories. From that moment on, the flaw is no longer just a theoretical risk: it becomes publicly known information, including to people with malicious intent. Projects that still use the vulnerable version remain exposed until the update is applied.&lt;/p&gt;

&lt;p&gt;The problem is that manually following this flow across multiple repositories, languages, and package managers is impractical. Without automation, this process tends to become inconsistent, delayed, or simply forgotten.&lt;/p&gt;

&lt;h2 id=&quot;what-is-dependabot&quot;&gt;What is Dependabot&lt;/h2&gt;

&lt;p&gt;Dependabot is a GitHub tool that monitors repository dependencies and automatically opens pull requests when it identifies newer versions or known vulnerabilities. It runs directly within GitHub’s infrastructure, without requiring external servers to be installed or configured.&lt;/p&gt;

&lt;p&gt;The tool works in two complementary modes. The first is security monitoring. When a vulnerability is registered in the GitHub Advisory Database and affects a dependency present in the project, GitHub can generate an alert in the repository. When security updates are enabled, Dependabot can automatically open a pull request with the fixed version or with an update that removes the identified exposure.&lt;/p&gt;

&lt;p&gt;The second mode is version updates. Regardless of vulnerabilities, Dependabot periodically checks whether newer dependency versions are available and proposes updates automatically.&lt;/p&gt;

&lt;p&gt;In both cases, the result is a pull request that the team can review, test, and approve. The final decision remains with people. What changes is that the work of monitoring and preparing the update is no longer manual.&lt;/p&gt;

&lt;h2 id=&quot;how-to-configure-it&quot;&gt;How to configure it&lt;/h2&gt;

&lt;p&gt;Dependabot is configured through a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dependabot.yml&lt;/code&gt;, which must be placed inside the repository’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;A basic example for projects that use npm is:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;updates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;package-ecosystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;npm&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;weekly&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This file instructs Dependabot to check, once a week, the npm dependencies declared at the root of the project. From this configuration onward, the tool starts tracking available new versions and can automatically open pull requests when updates are available.&lt;/p&gt;

&lt;p&gt;In repositories with many dependencies, a slightly more controlled configuration may be more appropriate:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;updates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;package-ecosystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;npm&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;weekly&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;monday&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;09:00&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;America/Sao_Paulo&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;open-pull-requests-limit&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, besides defining the frequency, we also specify the day, time, and time zone for the check. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open-pull-requests-limit&lt;/code&gt; parameter helps control the number of pull requests opened at the same time, preventing the first execution of the tool from generating more work than the team can review.&lt;/p&gt;

&lt;p&gt;The configuration also accepts relevant variations: it is possible to ignore specific packages, define labels, configure commit messages, group updates, and declare multiple ecosystems within the same repository if the project uses different languages or package managers in different directories.&lt;/p&gt;

&lt;p&gt;In the case of the LESIS blog, for example, the configuration needed to cover more than one ecosystem. In addition to Ruby dependencies managed by Bundler, we also configured Dependabot to monitor updates to GitHub Actions workflows:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;updates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;package-ecosystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bundler&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;weekly&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;monday&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;03:00&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;America/Sao_Paulo&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;open-pull-requests-limit&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;dependencies&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ruby&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;commit-message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;chore(deps)&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;prefix-development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;chore(deps-dev)&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;scope&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dependency-type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;production&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;update-types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;minor&quot;&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;patch&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;production-major&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dependency-type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;production&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;update-types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;major&quot;&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;package-ecosystem&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;github-actions&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;weekly&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;monday&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;04:00&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;America/Sao_Paulo&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;open-pull-requests-limit&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;dependencies&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;github-actions&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;commit-message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ci&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;scope&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;minor-patch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;update-types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;minor&quot;&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;patch&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;major&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;update-types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;major&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundler&lt;/code&gt; monitors the project’s gems, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github-actions&lt;/code&gt; monitors the actions used in the repository’s workflows. The configuration also defines labels specific to each ecosystem, commit prefixes, and update groups.&lt;/p&gt;

&lt;p&gt;Grouping helps organize the volume of pull requests. For production dependencies, for example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;minor&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;patch&lt;/code&gt; updates can be grouped separately from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;major&lt;/code&gt; updates, which tend to require more attention during review. The same reasoning was applied to GitHub Actions updates.&lt;/p&gt;

&lt;p&gt;When the task was assigned, the starting point was studying the tool. Dependabot was not familiar to us at that point, so before any configuration it was necessary to understand how it worked, what it monitored, and which options were available. This study time was necessary and worthwhile: after understanding the tool, the configuration itself was straightforward. GitHub’s documentation clearly covers the most common cases, and the first automatically generated pull requests appeared shortly after the file was added.&lt;/p&gt;

&lt;h2 id=&quot;limitations-we-found&quot;&gt;Limitations we found&lt;/h2&gt;

&lt;p&gt;An important lesson from the process was that Dependabot does not support every package manager. In our case, some projects use CPAN, Perl’s dependency manager, which is not included in the list of ecosystems supported by the tool. In those repositories, automated monitoring is not possible with Dependabot, and tracking needs to be done by other means.&lt;/p&gt;

&lt;p&gt;For Perl projects, &lt;a href=&quot;https://github.com/lesis-lat/bunkai&quot;&gt;Bunkai&lt;/a&gt;, a software composition analysis tool developed by LESIS, covers part of this gap by identifying outdated dependencies and known vulnerabilities in projects that use CPAN.&lt;/p&gt;

&lt;p&gt;It is also worth mentioning that Dependabot is not the only tool available for this type of automation. A well-known open source alternative is &lt;a href=&quot;https://github.com/renovatebot/renovate&quot;&gt;Renovate&lt;/a&gt;, a tool for automated dependency updates. Like Dependabot, Renovate identifies outdated dependencies and can open pull requests with the necessary updates.&lt;/p&gt;

&lt;p&gt;Renovate can be especially interesting for teams that do not use GitHub, since it was designed to work across different code hosting platforms, such as GitLab, Bitbucket, Azure DevOps, Forgejo, and Gitea. It is also often considered when a project requires greater configuration flexibility, more specific update policies, or more customized workflows.&lt;/p&gt;

&lt;p&gt;In our case, Dependabot was a natural choice because it is already integrated with GitHub and requires little initial configuration. Still, for projects outside the GitHub ecosystem or with more complex automation needs, Renovate is an alternative worth evaluating.&lt;/p&gt;

&lt;p&gt;Before configuring the tool, it is worth checking whether the package managers used in the project are among the supported ones. The official list of compatible ecosystems is available in GitHub’s documentation and covers the most common cases, such as npm, pip, Maven, Gradle, Composer, and RubyGems, among others, but it is not universal.&lt;/p&gt;

&lt;p&gt;Another point that deserves attention is the initial volume of pull requests. In repositories with many dependencies that have not been updated for some time, the first Dependabot execution can generate a significant number of proposals at once. This can overload the review flow if no limit is configured. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open-pull-requests-limit&lt;/code&gt; parameter in the configuration file helps control this behavior.&lt;/p&gt;

&lt;h2 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h2&gt;

&lt;p&gt;Dependency management is not just an internal organization issue. It is a security issue with external consequences. Outdated libraries are a documented and actively exploited attack vector. In many cases, the difference between a vulnerable project and a more secure one lies in the existence of a process that keeps dependencies monitored and updated.&lt;/p&gt;

&lt;p&gt;Dependabot does not replace a comprehensive security posture, but it solves a specific part of the problem reliably and with a low configuration cost. It turns a task that would tend to be forgotten into a continuous, auditable process integrated into the team’s normal workflow.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Our experience configuring Dependabot in our repositories was positive. The initial effort is small, the integration with GitHub is native, and the result, continuously monitored dependencies, more than justifies the time invested.&lt;/p&gt;

&lt;p&gt;Limitations exist and should be understood before setting expectations. Not all ecosystems are supported, and the volume of pull requests may require configuration fine-tuning. But for compatible repositories, the tool delivers exactly what it promises.&lt;/p&gt;

&lt;p&gt;More than a convenience, keeping dependencies up to date is a responsibility. Automation does not eliminate this responsibility; it helps ensure that it is fulfilled consistently.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/code-security/dependabot&quot;&gt;Dependabot documentation - GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem&quot;&gt;Supported package ecosystems - GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/advisories&quot;&gt;GitHub Advisory Database&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cve.org/&quot;&gt;Common Vulnerabilities and Exposures - CVE&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/lesis-lat/bunkai&quot;&gt;Bunkai - SCA tool for Perl&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/renovatebot/renovate&quot;&gt;Renovate - Automated dependency update tool&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
    <author>
      <name>Maria Eduarda</name>
    </author>
    <category term="Guides" />
    <summary type="html">Understand how Dependabot automates vulnerability monitoring and dependency updates directly on GitHub.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Choosing the right method for sharing identifiers</title>
    <link href="https://blog.lesis.lat/blog/choosing-the-right-method-for-sharing-identifiers/" rel="alternate" type="text/html" title="Choosing the right method for sharing identifiers" />
    <link href="https://blog.lesis.lat/assets/audio/choosing-sharing-identifiers/en.mp3" rel="enclosure" type="audio/mpeg" length="8113238" />
    <published>2026-05-05T08:00:00-03:00</published>
    <updated>2026-05-05T08:00:00-03:00</updated>
    <id>https://blog.lesis.lat/blog/choosing-the-right-method-for-sharing-identifiers-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/blog/choosing-the-right-method-for-sharing-identifiers/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Some security problems appear precisely when two legitimate companies want to collaborate.&lt;/p&gt;

&lt;p&gt;Imagine a digital financial institution with a very large customer base and a marketplace preparing a promotion exclusively for that institution’s customers. During the campaign, the marketplace needs to know which people are also customers of the financial institution so it can apply the benefit correctly.&lt;/p&gt;

&lt;p&gt;The problem seems simple: one company has the list of eligible customers, the other has its own user base, and the promotion should apply only to the intersection between the two datasets.&lt;/p&gt;

&lt;p&gt;But there is an important question: how can this comparison be allowed without exposing more personal data than necessary?&lt;/p&gt;

&lt;h2 id=&quot;the-scenario&quot;&gt;The Scenario&lt;/h2&gt;

&lt;p&gt;The business need was direct: a promotional campaign should be offered by a commercial partner only to customers of the financial institution.&lt;/p&gt;

&lt;p&gt;Because the financial institution had a customer base larger than the partner’s base, simply sending all customer CPFs to the partner would allow the comparison, but it would expose far more people than necessary.&lt;/p&gt;

&lt;p&gt;The first proposal from the team responsible for the integration was to encrypt a file containing all eligible customer CPFs and transmit it through a secure channel. The partner would receive the file, decrypt it, compare it with its own base, and identify which users were eligible.&lt;/p&gt;

&lt;p&gt;From a transport perspective, this seems reasonable. The file would be protected during transmission. The channel could be authenticated. Access could be restricted.&lt;/p&gt;

&lt;p&gt;But the main problem was not only transport. The problem was minimization.&lt;/p&gt;

&lt;p&gt;At the end of the process, the partner would have access to a complete list of CPFs from customers of the financial institution, including people who might not even have an account in the marketplace, might never participate in the campaign, and might never need to be known by the partner. Encryption would protect the file in transit, but it would not reduce the data revealed to the recipient.&lt;/p&gt;

&lt;h2 id=&quot;encryption-solves-another-problem&quot;&gt;Encryption Solves Another Problem&lt;/h2&gt;

&lt;p&gt;Encryption is a confidentiality tool. It protects data from people who should not access it during storage or transmission. If an encrypted file is intercepted by someone without the key, its content remains protected.&lt;/p&gt;

&lt;p&gt;But in many integration flows, the legitimate recipient needs to open the file. After decrypting it, the recipient can see the original values.&lt;/p&gt;

&lt;p&gt;In this case, encryption answered the question:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How can the list of CPFs be sent without third parties reading the file in transit?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But there was a second question that still had to be answered:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How can the partner discover only which customers already exist in its own base, without receiving the complete list from the financial institution?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These questions may look similar, but they are different problems. The first is about confidentiality in transit and remained necessary. The second is about data minimization and exposure to the recipient. The point was not to replace encryption, but to recognize that it had to be combined with a design that revealed less information.&lt;/p&gt;

&lt;h2 id=&quot;where-hashes-help&quot;&gt;Where Hashes Help&lt;/h2&gt;

&lt;p&gt;A cryptographic hash transforms an input into a fixed-size output. For the same input, the result is always the same. For different inputs, the results are expected to be different. Good hash algorithms also make it impractical to recover the original input from the output, as long as the input has enough entropy.&lt;/p&gt;

&lt;p&gt;This deterministic property allows comparison without transmitting the original value.&lt;/p&gt;

&lt;p&gt;For example, instead of sharing:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;123.456.789-10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;a company could share:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sha256(&quot;12345678910&quot;) = 01b6...f3a9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the partner normalizes its own CPFs the same way and calculates the same hash, it can compare the hashes. When there is equality, there is a CPF in common.&lt;/p&gt;

&lt;p&gt;The privacy gain is intuitive: instead of directly revealing CPFs, the companies compare derived representations. The partner should only be able to recognize CPFs it already knows, because it needs to have the original value in its own base to generate the same hash.&lt;/p&gt;

&lt;p&gt;This idea is useful, but there is an important trap.&lt;/p&gt;

&lt;h2 id=&quot;cpf-is-enumerable&quot;&gt;CPF Is Enumerable&lt;/h2&gt;

&lt;p&gt;Developers often learn about hashes in the context of passwords. In that context, the input is ideally secret, chosen by the user, and hard to guess. Even then, passwords require specific algorithms such as Argon2, bcrypt, or scrypt, as well as salt, computational cost, and good storage policies.&lt;/p&gt;

&lt;p&gt;CPF is different.&lt;/p&gt;

&lt;p&gt;CPF has a known format, a limited number of combinations, and check digits. This means an attacker can generate many candidate CPFs, calculate their hashes, and compare them with a leaked list. This is the principle behind dictionary attacks or rainbow tables.&lt;/p&gt;

&lt;p&gt;For that reason, the following approach is weak:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sha256(cpf_normalizado)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It does not reveal the CPF directly, but it should not be treated as anonymization either. For predictable identifiers, a simple hash is often reversible through brute force or precomputation.&lt;/p&gt;

&lt;p&gt;This point is essential: hash is not magic. Security depends both on the algorithm and on the nature of the input.&lt;/p&gt;

&lt;h2 id=&quot;normalization-matters&quot;&gt;Normalization Matters&lt;/h2&gt;

&lt;p&gt;Before any comparison, both sides need to arrive at exactly the same representation of the identifier.&lt;/p&gt;

&lt;p&gt;For CPF, this usually means removing punctuation, validating length, preserving leading zeroes, and rejecting invalid or malformed values. Otherwise, the same CPF may produce different hashes:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;123.456.789-10
12345678910
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These two strings look equivalent to a person, but they are different inputs for a hash algorithm.&lt;/p&gt;

&lt;p&gt;Every comparison strategy needs to document normalization before documenting the algorithm. Without that, the process produces false negatives, operational inconsistency, and audit difficulties.&lt;/p&gt;

&lt;h2 id=&quot;salt-does-not-always-solve-it&quot;&gt;Salt Does Not Always Solve It&lt;/h2&gt;

&lt;p&gt;A common reaction is to add salt:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sha256(salt || cpf_normalizado)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In password storage, salt is fundamental because it prevents the same value from producing the same hash across different databases and makes generic precomputed tables less useful. But in a matching process between two companies, both parties need to generate the same result for the same CPF.&lt;/p&gt;

&lt;p&gt;If each company uses a different salt, the hashes will not match. If the salt is shared between the companies, it helps against some external precomputed tables, but it does not prevent a party with access to the salt from enumerating candidate CPFs and calculating their hashes.&lt;/p&gt;

&lt;p&gt;Salt is useful, but it does not solve the problem of predictable identifiers in a matching integration by itself.&lt;/p&gt;

&lt;h2 id=&quot;hmac-with-an-api-as-a-pragmatic-path&quot;&gt;HMAC with an API as a Pragmatic Path&lt;/h2&gt;

&lt;p&gt;A better alternative than a simple hash is to use HMAC:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hmac_sha256(secret_key, normalized_cpf)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;HMAC uses a secret key in the calculation. Without the key, a third party that obtains the list of HMACs cannot easily calculate the corresponding values for candidate CPFs. This significantly reduces the risk of offline attacks by third parties.&lt;/p&gt;

&lt;p&gt;But there is a design question. If the key is shared with the partner so it can calculate HMACs over its own base, the partner can also calculate HMACs for candidate CPFs on its own. This may be acceptable in some scenarios, as long as the key is specific to the campaign, rotated, scoped, and covered by audit controls, but it changes the trust model.&lt;/p&gt;

&lt;p&gt;A pragmatic path would be to combine HMAC with an API controlled by the financial institution. In this design, the marketplace prepares its own base, normalizes the CPFs, calculates the derived identifiers according to the campaign specification, and sends a batch to the API. The financial institution compares those values against an equivalent representation of its own base and returns only the result needed for the campaign. That result does not need to be a new list of CPFs. It can be an eligibility flag associated with the marketplace’s own users.&lt;/p&gt;

&lt;p&gt;A simple example would be:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Marketplace sends:
user_id=10, hmac_cpf=aaa
user_id=20, hmac_cpf=bbb
user_id=30, hmac_cpf=ccc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After the comparison, the API could respond:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;user_id=10, eligible=true
user_id=20, eligible=false
user_id=30, eligible=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this, the marketplace can apply the campaign inside its own base, but it does not receive the complete list of CPFs from the financial institution. CPF is used as the technical comparison key, but the operational result is an eligibility flag.&lt;/p&gt;

&lt;p&gt;This approach changes the problem into a controlled query model. If the API allows arbitrary CPFs to be tested, even with authentication and HMAC, it can become an eligibility oracle. For that reason, this type of solution needs additional controls: accepting only batches associated with the partner’s declared base, limiting reprocessing, auditing queries, enforcing campaign windows, and clearly defining what may be tested. In other words, HMAC and an API can form a practical solution, but the guarantee then depends on governance and operational control. For scenarios that require a stronger technical guarantee about what each party learns, there is a more sophisticated approach.&lt;/p&gt;

&lt;h2 id=&quot;a-more-sophisticated-alternative-private-set-intersection&quot;&gt;A More Sophisticated Alternative: Private Set Intersection&lt;/h2&gt;

&lt;p&gt;A more sophisticated alternative for this type of problem is known as private set intersection, or PSI.&lt;/p&gt;

&lt;p&gt;In simple terms, PSI is a family of cryptographic protocols that allows two parties to compare sets and discover an intersection without sharing the sets in plaintext. The idea is not to “encrypt a list and hand it to the other side”. The idea is to run a process in which each party keeps its own set and participates in a protocol-based comparison.&lt;/p&gt;

&lt;p&gt;A small example helps visualize the objective. Suppose the financial institution has the CPFs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;111&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;222&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;333&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;444&lt;/code&gt;. The partner has the CPFs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;222&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;444&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;555&lt;/code&gt;. The result needed for the campaign is to know that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;222&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;444&lt;/code&gt; are in both sets. The partner does not need to learn that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;111&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;333&lt;/code&gt; exist in the financial institution’s base. The financial institution also does not need to learn that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;555&lt;/code&gt; exists in the partner’s base, unless the protocol or campaign design requires it.&lt;/p&gt;

&lt;p&gt;In a common file-based comparison, one party receives a list and computes the intersection locally. This is simple, but it forces that party to see values that are not part of the final result. In an API-based query, on the other hand, the financial institution can avoid handing its entire base to the marketplace, but it starts observing the batch that the marketplace is querying. That may be acceptable. But if the requirement is also to reduce what the financial institution learns about the marketplace’s base, a centralized API may not be enough.&lt;/p&gt;

&lt;p&gt;There are different ways to implement PSI. Some use public-key cryptography, others use oblivious transfer, garbled circuits, or constructions based on hashing and ephemeral keys. The mathematical detail changes depending on the protocol, but the security objective remains: allow matching between sets without turning an integration into broad database sharing.&lt;/p&gt;

&lt;p&gt;It is also important to observe that PSI does not define only “how to compare”. It helps define “who learns what”. In some designs, both parties learn the intersection. In others, only one party learns which users are eligible. For the promotion case, this second model makes more sense: the marketplace needs to apply the benefit inside its own base, while the financial institution does not necessarily need to learn which customers are also in the marketplace.&lt;/p&gt;

&lt;p&gt;Applied to the promotion case, the desired result may not have been a new list of CPFs shared between companies. It could simply be a way for the partner to mark, inside its own base, which users are eligible. This distinction reduces exposure because it keeps the focus on the operational result of the campaign, not on the circulation of identifiers.&lt;/p&gt;

&lt;p&gt;PSI, however, does not eliminate every risk. If one party can freely choose the input set, it can still try to test identifiers that do not belong to its legitimate base. For that reason, PSI does not replace contracts, audits, scope controls, or process validation. What it improves is another point: it reduces the need for one party to hand over its entire base in plaintext, and it can also reduce how much the other party observes during the comparison.&lt;/p&gt;

&lt;p&gt;In practice, PSI is more complex than an API with HMAC. It requires an appropriate library, understanding of the protocol, care with normalization, error handling, auditing, performance testing, and evaluation of what each party learns during the process. Not every case requires this level of sophistication. But it should be considered when the requirement is not only to prevent the marketplace from receiving the financial institution’s entire base, but also to reduce how much the financial institution learns about the marketplace’s set during the matching process.&lt;/p&gt;

&lt;h2 id=&quot;a-practical-hierarchy-of-solutions&quot;&gt;A Practical Hierarchy of Solutions&lt;/h2&gt;

&lt;p&gt;Not every integration needs the same level of protection. A pragmatic way to think about it is to organize the options by exposure reduction.&lt;/p&gt;

&lt;p&gt;An encrypted file with CPFs in transit protects against interception, but reveals the complete list to the recipient. It is simple, but weak in minimization.&lt;/p&gt;

&lt;p&gt;Simple hashes of CPFs avoid casual direct exposure, but they are vulnerable to enumeration because CPF is predictable. They should not be treated as anonymization.&lt;/p&gt;

&lt;p&gt;HMAC with a controlled key improves protection against third parties and leaks of the derived list, but requires strong key governance. Instead of sharing the key with the partner, an authenticated API using HMAC can be a practical alternative when the goal is to answer eligibility without handing over the financial institution’s entire base, as long as it does not allow arbitrary queries without control.&lt;/p&gt;

&lt;p&gt;PSI or an equivalent protocol is a more sophisticated option when there is also interest in reducing what the financial institution learns about the set queried by the marketplace, in addition to avoiding delivery of the entire base to the partner.&lt;/p&gt;

&lt;p&gt;This hierarchy helps explain why the initial answer of “let’s encrypt the file” was insufficient. The question was not only how to transport data securely. The question was how much data needed to be revealed to achieve the objective.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;A security review for this type of sharing should begin with simple questions. Before choosing an algorithm, it is necessary to understand the exact objective of the comparison, who needs to learn the result, and which format that result needs to have. Does the result need to be a list of CPFs, a list of eligible users, a boolean per user, or only an aggregated count?&lt;/p&gt;

&lt;p&gt;It is also necessary to evaluate which data from people outside the intersection would be exposed by the proposed solution. This is a central question, because a solution may look secure because it uses encryption and still reveal a much larger set than necessary to the legitimate recipient.&lt;/p&gt;

&lt;p&gt;Another important point is the nature of the identifier being used. If the identifier is predictable or has low entropy, a simple hash should not be treated as strong protection. The existence of documented normalization also needs to be verified, because small formatting differences can break the comparison or create exceptions that are hard to audit.&lt;/p&gt;

&lt;p&gt;Finally, the review should cover who controls keys, salts, or secrets, whether the process is auditable and reproducible, and whether intermediate files and results have limited retention. These questions prevent the discussion from being limited to “is the file encrypted?”. Transport security matters, but minimization, purpose, retention, and the trust model matter just as much.&lt;/p&gt;

&lt;p&gt;Encryption, hashes, HMAC, APIs, and PSI are different tools for different problems. The right decision depends less on familiarity with a specific technique and more on what each party needs to learn during the process.&lt;/p&gt;

&lt;p&gt;The main lesson from this case is that “transmitting securely” is not the same as “revealing the minimum necessary”. In integrations between companies, especially when personal identifiers are involved, the architecture should start from minimization: who needs to know what, for how long, and with which technical guarantee?&lt;/p&gt;

&lt;p&gt;Sometimes, the best solution is not to encrypt the file better. It is to avoid having that file exist in that form.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Cryptographic_hash_function&quot;&gt;Cryptographic hash function - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/HMAC&quot;&gt;Hash-based message authentication code - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Private_set_intersection&quot;&gt;Private set intersection - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://csrc.nist.gov/pubs/sp/800/107/r1/final&quot;&gt;NIST SP 800-107 Rev. 1 - Recommendation for Applications Using Approved Hash Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Case Study" />
    <summary type="html">How to choose between encryption, hashes, HMAC, APIs, and PSI when comparing datasets with personal identifiers.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Orientations for PII obfuscation and masking</title>
    <link href="https://blog.lesis.lat/blog/orientations-for-pii-obfuscation-and-masking/" rel="alternate" type="text/html" title="Orientations for PII obfuscation and masking" />
    <link href="https://blog.lesis.lat/assets/audio/pii-obfuscation-and-masking/en.mp3" rel="enclosure" type="audio/mpeg" length="6692172" />
    <published>2026-05-04T10:00:00-03:00</published>
    <updated>2026-05-04T10:00:00-03:00</updated>
    <id>https://blog.lesis.lat/blog/orientations-for-pii-obfuscation-masking-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/blog/orientations-for-pii-obfuscation-and-masking/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Many applications handle personally identifiable information (PII): names, addresses, email addresses, phone numbers, identification numbers, device identifiers, and other data that can identify a person directly or indirectly.&lt;/p&gt;

&lt;p&gt;When this information appears in application screens, logs, reports, support tools, notifications, or non-production environments, the system should avoid exposing more than the user or operator actually needs. One common way to reduce this exposure is to mask or obfuscate part of the value.&lt;/p&gt;

&lt;p&gt;This practice is familiar. A password reset flow may show only part of an email address. A support dashboard may show only the last digits of a phone number. A banking interface may show a partially hidden tax identifier. Even so, the details are often inconsistent. Different systems choose different visible characters, different quantities, and different formats.&lt;/p&gt;

&lt;p&gt;That inconsistency matters. If the same PII is masked differently across multiple applications, each application may reveal a different fragment. An attacker who can trigger or observe those flows may combine the fragments and reconstruct the original value.&lt;/p&gt;

&lt;p&gt;This article proposes practical orientations for masking common PII fields. The goal is not to define a universal legal standard, but to provide a technical baseline for engineers, security teams, and product teams that need consistent behavior when displaying or processing personal data.&lt;/p&gt;

&lt;h2 id=&quot;obfuscation-masking-and-anonymization&quot;&gt;Obfuscation, masking, and anonymization&lt;/h2&gt;

&lt;p&gt;The terms are often used interchangeably, but they do not mean the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Masking&lt;/strong&gt; is the replacement of part of a value with a fixed placeholder, usually to hide sensitive characters while preserving enough context for recognition. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user.name@example.com&lt;/code&gt; may become &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u*******e@e*****e.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Obfuscation&lt;/strong&gt; is a broader practice of making data harder to understand or interpret. Masking is a type of obfuscation, but not every form of obfuscation is masking. Obfuscation may also include tokenization, redaction, truncation, or format-preserving transformations.&lt;/p&gt;

&lt;p&gt;An example of obfuscation that is not only masking is replacing an email address with an opaque identifier. Instead of displaying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;heitor.gouvea@lesis.lat&lt;/code&gt; as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h***********a@l***s.lat&lt;/code&gt;, the system could display &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_8f3a91&lt;/code&gt;. In this case, the original value does not appear partially. It was replaced by an opaque identifier. This may be tokenization or pseudonymization, depending on how the mapping to the original value is maintained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anonymization&lt;/strong&gt; means transforming data so that an individual can no longer be identified, directly or indirectly, using reasonably available means. Partial masking should not be treated as anonymization by default. A masked email address or phone number may still be linkable to a person, especially when combined with other data.&lt;/p&gt;

&lt;p&gt;In practice, masking is most useful as a data minimization control. It reduces unnecessary exposure. It does not replace encryption, access control, audit logging, retention limits, or secure deletion.&lt;/p&gt;

&lt;h2 id=&quot;why-consistency-matters&quot;&gt;Why consistency matters&lt;/h2&gt;

&lt;p&gt;Consider a user who uses the same public alias across multiple social networks. An attacker wants to discover the email address associated with that alias. The attacker cannot see the full email, but can trigger password reset flows on several platforms.&lt;/p&gt;

&lt;p&gt;If each platform masks the email differently, the attacker may receive fragments like these:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Platform&lt;/th&gt;
      &lt;th&gt;Masked email&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Instagram&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h***********a@g***l.com&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Twitter&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*************@gmail.com&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LinkedIn&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*******ouvea@gm********&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Facebook&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hei**********@gma***om&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Telegram&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*****r.******ea@g*****.com&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/pii-obfuscation/email-reconstruction-en.svg&quot; alt=&quot;Diagram of email reconstruction from masked fragments&quot; width=&quot;1400&quot; height=&quot;900&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Figure 1:&lt;/strong&gt; fragments of the same email revealed by different masking rules can be combined to reconstruct the original value.&lt;/p&gt;

&lt;p&gt;Individually, each fragment may look harmless. Together, they can reveal enough structure to reconstruct the address.&lt;/p&gt;

&lt;p&gt;The same problem can happen inside one organization. If two applications read from the same database and mask a tax identifier differently, each application may expose a different part of the value. Over time, logs, tickets, emails, screenshots, and support conversations may accumulate enough fragments to weaken the protection.&lt;/p&gt;

&lt;p&gt;For this reason, masking should be treated as a shared policy decision, not as a local UI formatting detail. The same field should be masked in the same way across products, internal tools, APIs, and operational workflows.&lt;/p&gt;

&lt;h2 id=&quot;when-to-mask-pii&quot;&gt;When to mask PII&lt;/h2&gt;

&lt;p&gt;PII masking is useful whenever the full value is not required for the user, operator, or system action being performed.&lt;/p&gt;

&lt;p&gt;In support systems, agents often need to confirm that they are looking at the right customer, but they rarely need the complete tax identifier, full phone number, or full email address. Showing a limited fragment can be enough for verification while reducing exposure in the support interface.&lt;/p&gt;

&lt;p&gt;In backup restoration tests, teams may need realistic data shape and volume, but they should not need access to real customer identifiers. Masking or replacing PII during restore can preserve operational testing value without unnecessarily exposing personal data in non-production environments.&lt;/p&gt;

&lt;p&gt;In analytics and model training, production data may contain signals that synthetic data does not reproduce well. Even then, the default should be to remove, mask, aggregate, or tokenize PII unless the full value is strictly necessary and legally justified.&lt;/p&gt;

&lt;p&gt;In logs and observability systems, masking is especially important because logs are often copied, indexed, retained, exported, and accessed by broader operational groups. A field that is safe in a restricted production database may become risky when repeated across log pipelines.&lt;/p&gt;

&lt;h2 id=&quot;general-masking-principles&quot;&gt;General masking principles&lt;/h2&gt;

&lt;p&gt;Masking decisions should be guided by a few simple rules.&lt;/p&gt;

&lt;p&gt;Reveal the minimum needed to complete the workflow. If the user only needs to distinguish between two phone numbers, the last two or four digits may be enough. If an operator only needs to know that an email was sent to the expected domain, the full local part should not be visible.&lt;/p&gt;

&lt;p&gt;Keep the format recognizable when it helps usability. For example, preserving punctuation in a CPF or phone number can help users recognize the field type without exposing the full value.&lt;/p&gt;

&lt;p&gt;Avoid revealing high-value structural information. In some identifiers, certain digits have meaning. In Brazilian CPF numbers, for example, the last two digits are check digits and the ninth digit can indicate the issuing region. A masking rule should consider those semantics instead of hiding random characters.&lt;/p&gt;

&lt;p&gt;Use one masking rule per field type and apply it everywhere. If the policy for an email address is to reveal the first and last character of the local part and domain label, every application should use the same rule.&lt;/p&gt;

&lt;p&gt;Do not use partial masking as the only protection for sensitive storage. If the original value must be retained, protect it with access control, encryption where appropriate, strict auditability, and retention limits. Masking should primarily control exposure at the presentation, export, logging, or derived-data layer.&lt;/p&gt;

&lt;p&gt;Masking should happen as close as possible to the exposure surface: UI, logs, exports, messages, reports, and derived environments. It should not be confused with transforming the original value in the database without a clear need.&lt;/p&gt;

&lt;h2 id=&quot;recommended-patterns&quot;&gt;Recommended patterns&lt;/h2&gt;

&lt;p&gt;The following patterns provide a practical starting point. They are baseline examples, not universal rules. Each organization should validate fields according to jurisdiction, threat model, and operational need, but the key point is consistency.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Field&lt;/th&gt;
      &lt;th&gt;Raw value&lt;/th&gt;
      &lt;th&gt;Masked value&lt;/th&gt;
      &lt;th&gt;Orientation&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CPF&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;111.222.333-00&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;***.222.33*-**&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Hide the first three digits, the ninth digit, and the two check digits. Preserve punctuation only for readability.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Email&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user.name@example.com.br&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;u*******e@e*****e.com.br&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Reveal only the first and last character of the local part and primary domain label. Preserve public suffixes such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.com.br&lt;/code&gt; when needed for recognition.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Phone number&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(11) 91234-5678&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(11) 9****-**78&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Preserve country or area code only when needed for routing or recognition. Reveal as few ending digits as the workflow requires.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IPv4 address&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;203.0.113.42&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;203.0.113.***&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;For display, hide the host octet. For analytics, prefer aggregation by subnet when possible.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IPv6 address&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2001:db8:abcd:0012:0000:0000:0000:0001&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2001:db8:abcd:0012:****:****:****:****&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Preserve only the network prefix needed for operational use. Hide interface-specific segments.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Document number&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12.345.678-9&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;**.345.67*-*&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Treat national or regional document numbers like structured identifiers. Hide check digits and avoid exposing every semantic position.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Vehicle plate&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ABC1D23&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A**1*23&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Reveal only the minimum required for user recognition. Avoid exposing enough characters to uniquely identify the vehicle in small datasets.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IMEI&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;356938035643809&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;35693803*****09&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Reveal only the TAC or ending digits when operationally required. Avoid showing the complete device identifier in logs or support tools.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;These examples are intentionally conservative. The exact number of visible characters may change depending on the workflow, but each system should make that decision explicitly.&lt;/p&gt;

&lt;h2 id=&quot;implementation-guidance&quot;&gt;Implementation guidance&lt;/h2&gt;

&lt;p&gt;Masking logic should live in shared code, not be reimplemented independently in each screen or service. A small library or shared service reduces inconsistency and makes future policy changes easier.&lt;/p&gt;

&lt;p&gt;In organizations with multiple systems, the most practical way to ensure consistency is to adopt a standard masking library. Instead of explaining the full policy to every team and manually reviewing each implementation, the organization can recommend a single interface for fields such as tax identifiers, email addresses, phone numbers, IP addresses, and documents.&lt;/p&gt;

&lt;p&gt;This makes verification simpler. Instead of validating whether each project implemented every rule correctly, the review can check whether the project is using the approved library. The policy remains important, but its application becomes centralized, testable, and easier to evolve.&lt;/p&gt;

&lt;p&gt;In some companies, it may make sense to develop an internal library for this, especially when there are regulatory rules, local formats, audit requirements, or product-specific standards. That library can include tests, documentation, usage examples, and clear versioning for behavior changes.&lt;/p&gt;

&lt;p&gt;Changes to masking rules should be versioned and communicated because they may affect support, audit, tests, and integrations that depend on the displayed format. If a rule changes, it should be treated as a behavior change in the library, not as an invisible internal detail.&lt;/p&gt;

&lt;p&gt;For companies using LLMs or GenAI in development, the same principle also applies. The masking recommendation can be part of a skill, guideline, or context package used by internal assistants, instructing the model to use the standard library instead of generating new masking implementations for each project.&lt;/p&gt;

&lt;p&gt;Each masking function should receive a normalized value and return a masked representation. Normalization and masking are related but separate concerns. For example, a phone number may be normalized to E.164 format internally, while the display layer may format it for the user’s locale before applying the visible pattern.&lt;/p&gt;

&lt;p&gt;PII fields should be masked before they enter log, observability, or analytics pipelines. Once sensitive data has been indexed, replicated, and retained, remediation becomes much harder.&lt;/p&gt;

&lt;p&gt;Input length matters. Short values should not accidentally leak too much. If an email local part has only two characters, revealing the first and last character reveals the whole local part. In those cases, mask the whole segment or reveal only one character.&lt;/p&gt;

&lt;p&gt;The policy should also define what happens with invalid or unexpected values. A masking function should fail closed: if it cannot parse a value safely, it should return a fully masked or redacted representation rather than the original input.&lt;/p&gt;

&lt;p&gt;Finally, test masking behavior as security-relevant logic. Unit tests should cover normal values, short values, malformed values, internationalized formats, separators, empty values, and already-masked values. Regression tests are useful because accidental changes to masking can silently increase exposure.&lt;/p&gt;

&lt;h2 id=&quot;hashing-and-encryption-are-different-controls&quot;&gt;Hashing and encryption are different controls&lt;/h2&gt;

&lt;p&gt;Masking, hashing, and encryption solve different problems.&lt;/p&gt;

&lt;p&gt;Encryption protects confidentiality by transforming data with a cryptographic key. If the system needs to recover the original value, encryption may be appropriate for storage or transmission, depending on the threat model and key management.&lt;/p&gt;

&lt;p&gt;Hashing transforms data into a fixed-length digest. It is useful for integrity checks and, with proper password hashing algorithms, password storage. Plain hashes of predictable PII are often weak because many identifiers have small or enumerable search spaces. If hashing PII for matching or deduplication, use a keyed construction or another design appropriate to the risk.&lt;/p&gt;

&lt;p&gt;Masking changes what is displayed or exported. It does not protect the original value wherever that value still exists. A masked field in a UI does not mean the database is encrypted. A masked value in a log does not mean upstream systems stopped processing the original PII.&lt;/p&gt;

&lt;p&gt;Strong privacy engineering usually combines these controls: minimize collection, restrict access, mask display, encrypt sensitive storage and transport, audit usage, and delete data when it is no longer needed.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;PII masking is a practical control for reducing unnecessary exposure of personal data, but it only works well when it is intentional and consistent.&lt;/p&gt;

&lt;p&gt;The main risk is not only showing too many characters in one place. It is showing different characters in different places, allowing fragments to be combined. A shared masking policy helps prevent that failure mode and gives engineers a clear standard to apply across applications, logs, support tools, exports, and non-production data flows.&lt;/p&gt;

&lt;p&gt;Masking should be treated as part of a broader privacy and security program. It supports data minimization, improves user privacy, and reduces operational exposure, but it does not replace cryptography, access control, legal review, or sound data governance.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Personal_data&quot;&gt;Personal data - Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-122.pdf&quot;&gt;Guide to Protecting the Confidentiality of Personally Identifiable Information (PII) - NIST SP 800-122&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gov.br/esporte/pt-br/acesso-a-informacao/lgpd&quot;&gt;Lei Geral de Proteção de Dados Pessoais (LGPD)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Guides" />
    <summary type="html">Practical recommendations for masking common types of personally identifiable information without exposing useful fragments across systems.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Primitives and vulnerabilities</title>
    <link href="https://blog.lesis.lat/research/2026/04/26/primitives-and-vulnerabilities-en.html" rel="alternate" type="text/html" title="Primitives and vulnerabilities" />
    <link href="https://blog.lesis.lat/assets/audio/primitives-and-vulnerabilities/en.mp3" rel="enclosure" type="audio/mpeg" length="1842160" />
    <published>2026-04-26T16:20:00-03:00</published>
    <updated>2026-04-26T16:20:00-03:00</updated>
    <id>https://blog.lesis.lat/research/2026/04/26/primitives-and-vulnerabilities-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/research/2026/04/26/primitives-and-vulnerabilities-en.html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Last week, I tested the use of LLMs to support the vulnerability research process. It made me think: how can we “teach” a model to actually help find valid bugs?&lt;/p&gt;

&lt;p&gt;I reflected on my own process: how I analyze code, model threats, and reach the point where something becomes, in fact, a vulnerability.&lt;/p&gt;

&lt;h2 id=&quot;understanding-or-creating-primitives&quot;&gt;Understanding, or creating, primitives&lt;/h2&gt;

&lt;p&gt;In short, I realized that I try to answer three questions during analysis:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;What is this code, feature, component, or endpoint trying to do?&lt;/li&gt;
  &lt;li&gt;What does it actually do?&lt;/li&gt;
  &lt;li&gt;What is possible to do with it?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I choose a primitive: the smallest part of the system that performs an action, such as a function, endpoint, or component. First, I try to understand what it should do, using documentation, names, and context.&lt;/p&gt;

&lt;p&gt;Then I observe what it actually does: I run it, test valid and invalid inputs, inspect logs, and compare the result with the expected behavior.&lt;/p&gt;

&lt;p&gt;When I find a discrepancy between intent and reality, I ask: what can be done with this? From there, I map what a malicious actor could control, which actions they could execute, and what the impact would be.&lt;/p&gt;

&lt;p&gt;If that difference allows real impact, then it is a vulnerability.&lt;/p&gt;

&lt;p&gt;Small isolated discrepancies sometimes do not create relevant impact, but when chained they almost always reveal interesting vectors. After looking at the micro level, it is necessary to understand the macro level: how all these primitives can be combined.&lt;/p&gt;

&lt;p&gt;I am now using prompts generated from this understanding in my day-to-day work, in an automated way and integrated into my workflow. They help me understand components faster, suggest more contextual data inputs, propose test inputs, and translate discrepancies into possible scenarios.&lt;/p&gt;

&lt;h2 id=&quot;a-simple-example&quot;&gt;A simple example&lt;/h2&gt;

&lt;p&gt;In a past analysis, I tested a web application and identified a login screen. I ran several tests, including using a valid email with wrong passwords, trying to generate different feedback from the application to better understand the behavior of the flow.&lt;/p&gt;

&lt;p&gt;After more than five login attempts with the wrong password, the application returned a warning: the account was blocked because the login attempt limit had been exceeded.&lt;/p&gt;

&lt;p&gt;Through the first question, the intent seemed clear: this control existed to prevent brute-force attacks.&lt;/p&gt;

&lt;p&gt;Through the second question, the real behavior had important details. The lockout did not expire automatically, the user had no simple way to unblock their own account, and no email notification was sent warning about the lockout. To recover access, the user had to contact support.&lt;/p&gt;

&lt;p&gt;Through the third question, the possible impact became clearer. A malicious actor did not need to discover the password or access the account. It was enough to know or enumerate valid emails and perform login attempts with incorrect passwords to block real accounts. A control created to protect against brute force could be used as a denial-of-service primitive against legitimate users.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Thinking in primitives helps turn analysis into a more explicit process. Instead of looking at the system only as a large and complex set of features, I start observing small actions, comparing intent and reality, and then understanding how those differences can be used.&lt;/p&gt;

&lt;p&gt;This reasoning is useful for manual research and also for using LLMs. The better I can describe a primitive, its intent, its real behavior, and its possible impact, the better I can use models and automation to support the investigation process. In the end, finding vulnerabilities still depends on technical judgment, but structuring the path toward them makes that judgment clearer, more repeatable, and easier to communicate.&lt;/p&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Research" />
    <summary type="html">How thinking in primitives helps turn discrepancies between intent and real behavior into impact analysis.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Digital safety for children: preparing the next generation</title>
    <link href="https://blog.lesis.lat/community/2026/04/26/o-cibernauta-digital-safety-for-the-next-generation-en.html" rel="alternate" type="text/html" title="Digital safety for children: preparing the next generation" />
    <link href="https://blog.lesis.lat/assets/audio/digital-safety-for-children/en.mp3" rel="enclosure" type="audio/mpeg" length="1725159" />
    <published>2026-04-26T15:30:00-03:00</published>
    <updated>2026-04-26T15:30:00-03:00</updated>
    <id>https://blog.lesis.lat/community/2026/04/26/o-cibernauta-digital-safety-for-the-next-generation-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/community/2026/04/26/o-cibernauta-digital-safety-for-the-next-generation-en.html">&lt;p&gt;Information security needs to reach people earlier. For a long time, this subject was treated as something restricted to professionals, companies, and technical teams. But reality has changed: children grow up connected, use devices from an early age, talk in digital environments, play online, watch content, share information, and build an important part of their relationships in cyberspace.&lt;/p&gt;

&lt;p&gt;This environment creates enormous opportunities, but it also exposes children and families to risks that are not always easy to notice. Scams, social engineering, account theft, data exposure, disinformation, and unsafe habits are already part of everyday digital life. Preparing the next generation is therefore not only an educational choice. It is a collective responsibility.&lt;/p&gt;

&lt;p&gt;In this context, we came across &lt;a href=&quot;https://www.linkedin.com/company/ocibernauta/about/&quot;&gt;O Cibernauta&lt;/a&gt;, a project that uses children’s literature to bring children, parents, and educators closer to essential topics in digital safety and online citizenship. Its first book, &lt;a href=&quot;https://www.editorabrasport.com.br/index.php?route=product/product&amp;amp;product_id=1709&quot;&gt;O Cibernauta em A Super Senha Secreta&lt;/a&gt;, published by Editora Brasport, turns a technical subject - creating and protecting passwords - into an accessible, playful, and useful adventure for different ages.&lt;/p&gt;

&lt;p&gt;The idea is simple and powerful: teach early, in a light and practical way, so that digital safety becomes part of children’s education before problems appear. When a child understands why a password matters, why some information should not be shared, or why unusual requests deserve attention, they are not only learning a rule. They are starting to develop critical thinking for navigating an increasingly complex world.&lt;/p&gt;

&lt;p&gt;The project was created by Daniel Meirelles and Eduardo Argollo, who recognized within their own families how difficult it can be to talk about information security in a clear and practical way. From that concern, they built an initiative that connects technology, education, and care, showing that children and adults can learn together about digital protection.&lt;/p&gt;

&lt;p&gt;LESIS decided to support this movement in a concrete way. We bought 30 copies of the book and are donating them to people and institutions that can multiply this knowledge. We are also supporting the authors with connections for events, conversations, and possible partnerships that can help the project reach further.&lt;/p&gt;

&lt;p&gt;This action is small compared to the size of the challenge. Even so, it represents an important conviction: information security is not strengthened only through research, tools, audits, or incident response. It is also strengthened when we help a child better understand the digital environment where they already live.&lt;/p&gt;

&lt;p&gt;By supporting O Cibernauta, we support a vision of the future where security culture begins before professional life, before the first bank account, before the first incident. It begins with habits, conversations at home, schools, and spaces where children can learn with curiosity, imagination, and care.&lt;/p&gt;

&lt;p&gt;We want to contribute to a more mature, accessible, and human security ecosystem. This includes supporting professionals, technical communities, educational initiatives, and projects that look after those who are just beginning their relationship with technology.&lt;/p&gt;

&lt;p&gt;We will continue to seek ways to support initiatives that use education as a tool for transformation. Because protecting the future also means better preparing the people who will build it.&lt;/p&gt;

&lt;p&gt;Learn more about the project on &lt;a href=&quot;https://www.instagram.com/ocibernauta_/&quot;&gt;O Cibernauta’s Instagram&lt;/a&gt; and through &lt;a href=&quot;https://www.editorabrasport.com.br/index.php?route=product/product&amp;amp;product_id=1709&quot;&gt;Editora Brasport&lt;/a&gt;.&lt;/p&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Community" />
    <summary type="html">Information security needs to reach people earlier. For a long time, this subject was treated as something restricted to professionals, companies, and technical teams. But reality has changed: children grow up connected, use devices from an early age, talk in digital environments, play online, watch content, share information, and build an important part of their relationships in cyberspace.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">State machine for vulnerability management</title>
    <link href="https://blog.lesis.lat/research/2026/04/09/vuln-state-machine-en.html" rel="alternate" type="text/html" title="State machine for vulnerability management" />
    <link href="https://blog.lesis.lat/assets/audio/vulnerability-state-machine/en.mp3" rel="enclosure" type="audio/mpeg" length="1376983" />
    <published>2026-04-09T00:00:00-03:00</published>
    <updated>2026-04-09T00:00:00-03:00</updated>
    <id>https://blog.lesis.lat/research/2026/04/09/vuln-state-machine-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/research/2026/04/09/vuln-state-machine-en.html">&lt;p&gt;In many organizations, the vulnerability management process suffers from a recurring — yet frequently overlooked — problem: inconsistency in the definition and use of statuses. It is common to see the same statuses used with different meanings by distinct teams, or to find redundant, poorly defined, or unnecessary statuses that create more confusion than clarity. This compromises traceability, hinders communication across teams, makes reliable metrics impossible, and weakens the organization’s ability to respond to real risks.&lt;/p&gt;

&lt;p&gt;To address this scenario, adopting a formalized state machine is an effective strategy. In a clear and objective way, it represents the lifecycle of a vulnerability: each transition has a precise meaning, and all teams share the same understanding of what each status represents.&lt;/p&gt;

&lt;p&gt;The flow begins with &lt;strong&gt;Draft&lt;/strong&gt;, indicating a work-in-progress being built by the analyst or pentester. The vulnerability is still being documented and has not yet been submitted for triage — context, evidence, or technical details may be missing. After this stage, it moves to &lt;strong&gt;Identified&lt;/strong&gt;, where impact and priority are assessed. If forwarded for remediation, it takes on the &lt;strong&gt;Fixing&lt;/strong&gt; status, reflecting active mitigation work. During the process, it may be identified as a repeat of a previous case, transitioning to &lt;strong&gt;Duplicated&lt;/strong&gt;. If the analysis shows there is no real risk — for example, due to an infeasible exploitation condition — the status becomes &lt;strong&gt;False Positive&lt;/strong&gt;. In certain cases, the organization chooses to accept the risk, whether due to technical limitations, remediation cost, or low perceived impact; in these cases, the status is &lt;strong&gt;Accepted Risk&lt;/strong&gt;. After remediation, the finding enters &lt;strong&gt;Retest&lt;/strong&gt; for re-evaluation. If validated, the process concludes with the &lt;strong&gt;Fixed&lt;/strong&gt; status.&lt;/p&gt;

&lt;p&gt;The rigorous definition of this state machine is not bureaucracy: it is a necessary foundation. By eliminating ambiguities and redundancies, it improves collaboration across teams, reduces rework, and enables better-informed decisions. It also allows for the generation of consistent metrics, useful for identifying bottlenecks, measuring efficiency, and supporting audits.&lt;/p&gt;

&lt;p&gt;This proposed state machine is both lean and complete. It is the most effective model I have found in practice: simple enough for rapid adoption and comprehensive enough to cover the main scenarios. It has proven to be an excellent starting point for standardizing processes, strengthening governance, and facilitating integration between security, product, and engineering.&lt;/p&gt;

&lt;p&gt;Below is the diagram representing this flow:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/vuln-state-machine/state-machine.webp&quot; alt=&quot;State machine diagram of a vulnerability&apos;s lifecycle&quot; width=&quot;1327&quot; height=&quot;732&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Research" />
    <summary type="html">In many organizations, the vulnerability management process suffers from a recurring — yet frequently overlooked — problem: inconsistency in the definition and use of statuses. It is common to see the same statuses used with different meanings by distinct teams, or to find redundant, poorly defined, or unnecessary statuses that create more confusion than clarity. This compromises traceability, hinders communication across teams, makes reliable metrics impossible, and weakens the organization’s ability to respond to real risks.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Economic taxonomy of vulnerabilities</title>
    <link href="https://blog.lesis.lat/research/2026/02/08/vulnerability-price-en.html" rel="alternate" type="text/html" title="Economic taxonomy of vulnerabilities" />
    <link href="https://blog.lesis.lat/assets/audio/vulnerability-economics/en.mp3" rel="enclosure" type="audio/mpeg" length="8122838" />
    <published>2026-02-08T13:52:02-03:00</published>
    <updated>2026-02-08T13:52:02-03:00</updated>
    <id>https://blog.lesis.lat/research/2026/02/08/vulnerability-price-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/research/2026/02/08/vulnerability-price-en.html">&lt;p&gt;In cybersecurity, one of the most relevant questions and, at the same time, one of the hardest to answer precisely is: what is the financial cost of a vulnerability? Although there are several programs and practices focused on vulnerability management, the challenge of assigning a monetary value to each flaw, especially when it has not yet been exploited, remains underexplored from an objective and quantitative perspective.&lt;/p&gt;

&lt;p&gt;As attacks become more sophisticated, regulatory demands increase, and contemporary digital ecosystems grow in complexity, understanding the real cost of a vulnerability becomes essential. This understanding allows strategic information security decisions to be grounded not only in risk perception, but also in tangible economic analysis.&lt;/p&gt;

&lt;h2 id=&quot;context-and-analytical-data&quot;&gt;Context and analytical data&lt;/h2&gt;

&lt;p&gt;Historically, decisions related to prioritization and investment in security have been based on qualitative assessments and risk models built from hypothetical scenarios. Although useful, these approaches do not always provide strong arguments to justify resource allocation when compared with areas such as finance, product, or executive leadership.&lt;/p&gt;

&lt;p&gt;In this context, there is a need for analysis centered on the &lt;strong&gt;economic measurement of vulnerabilities&lt;/strong&gt;, using real data.
Based on a new data analysis, this study seeks to answer the following question more precisely:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the cost of a vulnerability, considering different severity levels and categories?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Answering this question provides important support for prioritization strategies, budget definition, and decision-making in information security.&lt;/p&gt;

&lt;h2 id=&quot;assumptions&quot;&gt;Assumptions&lt;/h2&gt;

&lt;p&gt;To conduct the analysis in an objective, clear, and decision-relevant way, the following assumptions were established. They define the study scope and support the adopted methodological approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Vulnerabilities have measurable economic value&lt;/strong&gt;: each vulnerability can be associated with a monetary value that reflects the effort required for its discovery and reporting, as well as the risks it represents. &lt;em&gt;bug bounty&lt;/em&gt; programs are treated as a market reference for this measurement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Vulnerability value increases with severity&lt;/strong&gt;: it is assumed that vulnerability severity, determined by impact and likelihood of exploitation, is directly related to cost. Critical vulnerabilities tend to imply greater risks and, therefore, higher costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Bug bounty rewards are valid proxies for cost estimation&lt;/strong&gt;: amounts paid in bug bounty programs are used as conservative estimates of vulnerability discovery cost. For greater rigor, the lowest value within each published reward range is always used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The focus is exclusively on the discovery phase&lt;/strong&gt;: the analysis does not include subsequent vulnerability lifecycle stages (such as remediation, revalidation, disclosure, or exploitation impact). It focuses only on the estimated cost of identification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Security resources are limited and require strategic allocation&lt;/strong&gt;: given that security budgets are finite, understanding per-vulnerability cost supports more effective decisions about effort and investment allocation.&lt;/p&gt;

&lt;h2 id=&quot;out-of-scope&quot;&gt;Out of scope&lt;/h2&gt;

&lt;p class=&quot;section-footer-note&quot;&gt;This study focuses on the economics of vulnerability discovery using public bug bounty data. It does not cover full vulnerability management design, tool/vendor evaluation, financial impact modeling of incidents, prescriptive budget allocation, or unknown vulnerability classes such as &lt;em&gt;zero-days&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;approach&quot;&gt;Approach&lt;/h2&gt;

&lt;p&gt;Inspired by the information asymmetry theory presented in &lt;strong&gt;&lt;em&gt;The Market for Lemons: Quality Uncertainty and the Market Mechanism&lt;/em&gt;&lt;/strong&gt; [1], this study starts from the premise that markets can reveal prices even under uncertainty about the quality of traded goods, as long as minimally stable signaling mechanisms exist.&lt;/p&gt;

&lt;p&gt;In information security, bug bounty programs function as these mechanisms by assigning differentiated monetary values to vulnerabilities according to perceived severity. Therefore, rewards are assumed to reflect, conservatively, a market value associated with vulnerability discovery and reporting, partially mitigating the asymmetry between those who identify vulnerabilities and those who bear their risk. Based on this premise, the methodology adopts a quantitative and comparative approach, using real bug bounty program data to estimate typical organizational payout levels across severity categories.&lt;/p&gt;

&lt;p&gt;Although rewards do not represent the total cost resulting from the existence or exploitation of a vulnerability, they provide a standardized, empirically observable &lt;em&gt;proxy&lt;/em&gt; for economic measurement. From an information asymmetry perspective, bug bounty programs operate as incentive markets in which price signals not only perceived vulnerability value, but also expected discovery effort. In this setting, independent researchers tend to invest rationally only when expected net payout exceeds marginal discovery, validation, and reporting costs, a necessary condition for profitability and operational continuity.&lt;/p&gt;

&lt;p&gt;When offered amounts are perceived as insufficient relative to attack surface complexity or validation requirements, engagement and report volume tend to decline. Symmetrically, organizations dynamically adjust rewards as attraction or disincentive mechanisms, increasing payouts when qualified reports are scarce or reducing them when reported vulnerability volume exceeds triage and remediation capacity.&lt;/p&gt;

&lt;p&gt;This cycle of entry, exit, and migration across programs works as a self-correcting mechanism. Therefore, while information asymmetry does not disappear entirely, it tends to remain residual and operationally acceptable, since persistent mismatches between required effort and payouts are quickly detected by researchers and repriced by the market.&lt;/p&gt;

&lt;h2 id=&quot;scope-of-the-analysis&quot;&gt;Scope of the analysis&lt;/h2&gt;

&lt;p&gt;The analysis is restricted to the vulnerability discovery stage, that is, the effort required to identify and report a vulnerability with technical evidence. Remediation, revalidation, and disclosure phases are out of scope.
Data is obtained exclusively from sources, prioritizing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bug bounty platforms with open information;&lt;/li&gt;
  &lt;li&gt;Rewards categorized by severity;&lt;/li&gt;
  &lt;li&gt;Multiple geographies, for greater representativeness.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;rationale-for-the-approach&quot;&gt;Rationale for the approach&lt;/h3&gt;

&lt;p&gt;By using market data (rewards effectively paid), this approach avoids the subjectivity typical of impact estimates and adopts a criterion recognized across sectors as a practical value reference. This method also enables future comparisons with other security investment models, such as penetration testing (pentests) or automated tools, providing a concrete basis to assess return on investment (ROI) and guide resource allocation.&lt;/p&gt;

&lt;h2 id=&quot;methodology&quot;&gt;Methodology&lt;/h2&gt;

&lt;p&gt;The methodology adopted in this study was designed to ensure transparency, reproducibility, and consistency in the analysis of data extracted from public bug bounty platforms. The main process steps are detailed below:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Data collection is performed using custom scrapers developed to extract information from active programs that:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;Disclose financial values assigned to vulnerabilities;&lt;/li&gt;
  &lt;li&gt;Clearly classify flaws by severity.&lt;/li&gt;
  &lt;li&gt;Programs from companies of different sizes and sectors, with national and international coverage, are included to capture a representative market view.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;After collection, data is filtered using the following exclusion criteria:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;VDP (Vulnerability Disclosure Program) initiatives that do not offer financial rewards;&lt;/li&gt;
  &lt;li&gt;Programs with inconsistent or incomplete severity or reward data;&lt;/li&gt;
  &lt;li&gt;Generic rewards without severity linkage.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;Rewards are classified into the following categories, based on program-provided descriptions or, when unavailable, criteria compatible with the CVSS standard:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Informational
Low
Medium
High
Critical
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Value normalization, to avoid distortions in the results:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;For value ranges (e.g., $1,000 to $5,000), the minimum value in the range is used, adopting a conservative approach;&lt;/li&gt;
  &lt;li&gt;All values are converted to U.S. dollars (USD), based on a reference exchange rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;analysis&quot;&gt;Analysis&lt;/h2&gt;

&lt;p&gt;A total of 808 bug bounty programs were analyzed, distributed across HackerOne, Bugcrowd, YesWeHack, Intigriti, BugHunt, and Bugpay. The sample includes active programs from organizations with different sizes, sectors, and geographic locations, providing a broad and representative view of the vulnerability reward market.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/price-vuln/distribution-by-platform.webp&quot; alt=&quot;Bar chart of the distribution of bug bounty programs by platform&quot; width=&quot;802&quot; height=&quot;573&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Figure 1:&lt;/strong&gt; distribution of bug bounty programs by platform.&lt;/p&gt;

&lt;p&gt;The data used in this analysis is available for reproduction and verification in the repository: &lt;a href=&quot;https://huggingface.co/datasets/lesis-lat/bug-bounty-programs-rewards/viewer/default/train&quot;&gt;https://huggingface.co/datasets/lesis-lat/bug-bounty-programs-rewards/viewer/default/train&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/price-vuln/visibility-by-type.webp&quot; alt=&quot;Chart of the distribution of bug bounty programs by visibility type, public versus private&quot; width=&quot;752&quot; height=&quot;713&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Figure 2:&lt;/strong&gt; distribution of bug bounty programs by visibility type (public vs private).&lt;/p&gt;

&lt;p&gt;The table below presents the minimum and maximum reward values observed in each severity category, highlighting the wide variation across programs and platforms. In particular, while low-severity vulnerabilities may have symbolic minimum rewards, upper ranges, especially for high and critical severities, reach significantly high values, reflecting different incentive strategies and risk perceptions.&lt;/p&gt;

&lt;table class=&quot;table-numeric&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Low&lt;/th&gt;
      &lt;th&gt;Medium&lt;/th&gt;
      &lt;th&gt;High&lt;/th&gt;
      &lt;th&gt;Critical&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Minimum&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;$1.00&lt;/td&gt;
      &lt;td&gt;$3.00&lt;/td&gt;
      &lt;td&gt;$5.00&lt;/td&gt;
      &lt;td&gt;$50.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Maximum&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;$2,000.00&lt;/td&gt;
      &lt;td&gt;$47,175.93&lt;/td&gt;
      &lt;td&gt;$58,969.91&lt;/td&gt;
      &lt;td&gt;$100,000.00&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Table 1:&lt;/strong&gt; minimum and maximum bug bounty reward values by severity (USD).&lt;/p&gt;

&lt;p&gt;These values define the full variation range in the observed data and serve as a basis for more robust statistical analyses.&lt;/p&gt;

&lt;p&gt;Additionally, central tendency analysis reveals more stable pricing patterns by severity. The table below presents mean, median, and mode values, indicating that despite relevant outliers, reward concentration occurs around well-defined levels for each category.&lt;/p&gt;

&lt;table class=&quot;table-numeric&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Severity&lt;/th&gt;
      &lt;th&gt;Mean&lt;/th&gt;
      &lt;th&gt;Median&lt;/th&gt;
      &lt;th&gt;Mode&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Low&lt;/td&gt;
      &lt;td&gt;$154.75&lt;/td&gt;
      &lt;td&gt;$100.00&lt;/td&gt;
      &lt;td&gt;$100.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Medium&lt;/td&gt;
      &lt;td&gt;$563.93&lt;/td&gt;
      &lt;td&gt;$350.00&lt;/td&gt;
      &lt;td&gt;$500.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;High&lt;/td&gt;
      &lt;td&gt;$1,825.83&lt;/td&gt;
      &lt;td&gt;$1,000.00&lt;/td&gt;
      &lt;td&gt;$1,000.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Critical&lt;/td&gt;
      &lt;td&gt;$4,601.35&lt;/td&gt;
      &lt;td&gt;$3,000.00&lt;/td&gt;
      &lt;td&gt;$3,000.00&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Table 2:&lt;/strong&gt; mean, median, and mode of rewards by severity (USD).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/price-vuln/platform-comparsion.webp&quot; alt=&quot;Chart comparing median rewards by platform and severity&quot; width=&quot;1680&quot; height=&quot;1014&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Figure 3:&lt;/strong&gt; median reward comparison by platform and severity.&lt;/p&gt;

&lt;p&gt;Comparing mean and median in light of &lt;em&gt;skewness&lt;/em&gt; coefficients indicates that the arithmetic mean does not adequately represent typical vulnerability reward behavior. All severity categories show &lt;strong&gt;high positive asymmetry&lt;/strong&gt; (&lt;em&gt;skewness&lt;/em&gt; ranging approximately from &lt;strong&gt;6.49 to 21.72&lt;/strong&gt;), evidencing distributions strongly concentrated at lower values, with few exceptionally high payouts. Although &lt;em&gt;skewness&lt;/em&gt; intensity varies across severities, especially in medium-severity vulnerabilities, this pattern explains the systematic divergence between mean and median and confirms &lt;strong&gt;mean sensitivity to extreme values&lt;/strong&gt;, making robust measures more appropriate for data interpretation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/price-vuln/price-by-severity-platforms.webp&quot; alt=&quot;Chart of typical vulnerability prices by severity across platforms&quot; width=&quot;750&quot; height=&quot;459&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Figure 4:&lt;/strong&gt; typical vulnerability prices by severity across platforms.&lt;/p&gt;

&lt;p&gt;Reward value variability was assessed through the &lt;strong&gt;interquartile range (IQR)&lt;/strong&gt;, which captures dispersion in the central core of the distribution. A consistent increase in typical variability is observed as vulnerability severity rises. For low-severity vulnerabilities, IQR is approximately &lt;strong&gt;USD 141.03&lt;/strong&gt;, indicating higher concentration of practiced values. This interval expands to &lt;strong&gt;USD 300.00&lt;/strong&gt; for medium severity, &lt;strong&gt;USD 1,410.30&lt;/strong&gt; for high severity, and &lt;strong&gt;USD 3,500.00&lt;/strong&gt; for critical vulnerabilities, showing a progressive widening of the range in which the most frequent rewards are concentrated. This behavior indicates that more severe vulnerabilities are associated not only with higher values, but also with greater economic uncertainty in pricing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/price-vuln/price-by-severity-type.webp&quot; alt=&quot;Chart of median rewards by severity in public versus private programs&quot; width=&quot;1680&quot; height=&quot;1073&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;content-caption&quot;&gt;&lt;strong&gt;Figure 5:&lt;/strong&gt; median rewards by severity for public vs private programs.&lt;/p&gt;

&lt;p&gt;Taken together, the results show that while consistent severity-based pricing patterns exist, the bug bounty market presents high heterogeneity and recurring extreme values. Even so, central tendency and dispersion statistics support the conclusion that vulnerability discovery has an observable economic value differentiated by severity level.&lt;/p&gt;

&lt;p&gt;Therefore, it is methodologically appropriate to state cost ranges by severity, provided these ranges are defined using robust measures such as median and interquartile range, and not interpreted as deterministic limits. Based on the analyzed data, low-severity vulnerability discovery has a typical cost concentrated between approximately &lt;strong&gt;USD 59 and USD 200&lt;/strong&gt;, with a median of &lt;strong&gt;USD 100&lt;/strong&gt;. For medium severity, the typical range lies between &lt;strong&gt;USD 200 and USD 500&lt;/strong&gt;, with a median around &lt;strong&gt;USD 350&lt;/strong&gt;. High-severity vulnerabilities show greater dispersion, with values concentrated between &lt;strong&gt;USD 590 and USD 2,000&lt;/strong&gt;, and a median of &lt;strong&gt;USD 1,000&lt;/strong&gt;, while critical vulnerabilities exhibit the highest cost ranges, approximately between &lt;strong&gt;USD 1,500 and USD 5,000&lt;/strong&gt;, with a median of &lt;strong&gt;USD 3,000&lt;/strong&gt;. These ranges reflect typical market behavior and highlight the progressive increase in economic uncertainty as vulnerability severity increases.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This study investigated the economics of vulnerability payouts using a quantitative approach based on real bug bounty program data. In this context, “cost” refers to the amount paid by organizations for accepted vulnerability reports, not the internal effort cost incurred by researchers to discover and report vulnerabilities. Starting from the premise that these programs operate as market mechanisms under information asymmetry, the analysis sought to objectively estimate how payout values vary by severity.&lt;/p&gt;

&lt;p&gt;Results show that vulnerability discovery has an observable economic value that is systematically differentiated by severity. Statistical analyses revealed strongly skewed distributions across all categories, with recurring extreme values, making arithmetic mean inadequate as a standalone representative metric. In contrast, robust measures such as median and interquartile range proved more appropriate for capturing typical market behavior.&lt;/p&gt;

&lt;p&gt;Based on these indicators, it was possible to estimate typical payout ranges associated with vulnerability discovery. Low-severity vulnerabilities show values concentrated between approximately &lt;strong&gt;USD 59 and USD 200&lt;/strong&gt;, with a median of &lt;strong&gt;USD 100&lt;/strong&gt;. For medium severity, the typical range lies between &lt;strong&gt;USD 200 and USD 500&lt;/strong&gt;, with a median around &lt;strong&gt;USD 350&lt;/strong&gt;. High-severity vulnerabilities concentrate between &lt;strong&gt;USD 590 and USD 2,000&lt;/strong&gt;, with a median of &lt;strong&gt;USD 1,000&lt;/strong&gt;, while critical vulnerabilities present the highest payout ranges, approximately between &lt;strong&gt;USD 1,500 and USD 5,000&lt;/strong&gt;, with a median of &lt;strong&gt;USD 3,000&lt;/strong&gt;. These ranges reflect typical market behavior and highlight the progressive increase in economic variability as severity rises.&lt;/p&gt;

&lt;p&gt;From an economic perspective, these findings reinforce the interpretation of bug bounty programs as incentive markets in which rewards function as signals balancing supply of specialized researcher effort and organizational demand for flaw discovery. In this environment, researchers tend to allocate effort only when expected return exceeds marginal cost, supporting profitability and sustained operation. Information asymmetry, while still present, is disciplined by migration dynamics across programs and tends to remain at low, acceptable levels. Although such rewards do not represent the total cost associated with the existence or exploitation of a vulnerability, they provide a standardized empirical proxy for the organizational payout associated with vulnerability identification and reporting.&lt;/p&gt;

&lt;p&gt;From a practical perspective, estimating &lt;strong&gt;Expected Vulnerability Discovery Cost (EVDC)&lt;/strong&gt; by severity provides objective support for prioritization decisions, budget planning, and security investment assessment. By translating vulnerabilities into measurable economic ranges, the study helps reduce exclusive dependence on qualitative assessments and brings vulnerability management closer to an economic logic comparable to other technology and risk investments.&lt;/p&gt;

&lt;p&gt;Finally, although limited to the discovery scope and bug bounty programs, this work establishes an empirical basis for future research exploring the relationship between discovery cost, remediation cost, and exploitation impact, as well as comparisons with other security investment models. In this sense, the analysis does not seek to provide deterministic values, but robust economic references for understanding and discussing vulnerability costs in contemporary digital environments.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.sfu.ca/~wainwrig/Econ400/akerlof.pdf&quot;&gt;https://www.sfu.ca/~wainwrig/Econ400/akerlof.pdf&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cl.cam.ac.uk/archive/rja14/Papers/sciecon2.pdf&quot;&gt;https://www.cl.cam.ac.uk/archive/rja14/Papers/sciecon2.pdf&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Research" />
    <summary type="html">In cybersecurity, one of the most relevant questions and, at the same time, one of the hardest to answer precisely is: what is the financial cost of a vulnerability? Although there are several programs and practices focused on vulnerability management, the challenge of assigning a monetary value to each flaw, especially when it has not yet been exploited, remains underexplored from an objective and quantitative perspective.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Considerations for writing security reports</title>
    <link href="https://blog.lesis.lat/guides/2026/01/15/considerations-for-writing-security-reports-en.html" rel="alternate" type="text/html" title="Considerations for writing security reports" />
    <link href="https://blog.lesis.lat/assets/audio/writing-security-reports/en.mp3" rel="enclosure" type="audio/mpeg" length="3607841" />
    <published>2026-01-15T15:15:10-03:00</published>
    <updated>2026-01-15T15:15:10-03:00</updated>
    <id>https://blog.lesis.lat/guides/2026/01/15/considerations-for-writing-security-reports-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/guides/2026/01/15/considerations-for-writing-security-reports-en.html">&lt;p&gt;After days or weeks of work, you have identified relevant security issues, gained a solid understanding of the assessed environment’s attack surface, and gathered sufficient evidence to demonstrate real business risks. From a technical standpoint, the assessment is complete. What remains is to turn that knowledge into a report that is clear, actionable, and easy to understand.&lt;/p&gt;

&lt;p&gt;In any information security activity—technical testing, audits, maturity assessments, or risk analyses—the report is the primary artifact delivered to the client. It is the vehicle through which technical findings are translated into strategic decisions. Without a solid report, even the best technical work loses its impact.&lt;/p&gt;

&lt;p&gt;Security reports document objectives, context, scope, methodology, findings, risks, and recommendations. The amount of information involved is significant, which is why how it is organized and presented is just as important as the content itself. This text does not propose a fixed template, but rather practical guidance to improve the quality of written communication in information security.&lt;/p&gt;

&lt;h3 id=&quot;be-clear-about-the-objective&quot;&gt;Be clear about the objective&lt;/h3&gt;

&lt;p&gt;Every security report should start with a simple question: what was the objective of the assessment? Without a clear answer, the document tends to become a collection of disconnected findings. It is common to focus excessively on technical details. While they are part of the work, the goal of a security assessment is not to demonstrate expertise, but to reduce risk. The report should reflect that intent.&lt;/p&gt;

&lt;p&gt;Keeping the objective in mind while writing helps define what to include, the appropriate level of detail, and the overall tone. Creating an outline before writing often makes this process easier, reducing repetition, avoiding gaps, and making the writing more fluid.&lt;/p&gt;

&lt;h2 id=&quot;understand-your-audience&quot;&gt;Understand your audience&lt;/h2&gt;

&lt;p&gt;Security reports rarely have a single reader. Executives, managers, and technical teams often consume the same document with different expectations. For this reason, the text should be understandable even to those who do not master technical details. This does not mean oversimplifying, but rather explaining concepts and impacts clearly and with proper context.&lt;/p&gt;

&lt;p&gt;Including an executive summary is a well-established practice. It provides a high-level view of the main risks and the overall security posture and is often the only section read by decision-makers. The remainder of the report can be more technical, as long as it provides sufficient context and does not assume prior knowledge.&lt;/p&gt;

&lt;h2 id=&quot;be-selective-with-content&quot;&gt;Be selective with content&lt;/h2&gt;

&lt;p&gt;A security report does not need to be long to be complete; it needs to be relevant. Everything included should contribute to understanding risk or supporting decision-making. Raw tool output, repetitive screenshots, or redundant information tend to hinder readability and dilute the main message.&lt;/p&gt;

&lt;p&gt;The focus should be on evidence that supports clear conclusions. Continuously evaluating how each piece of information connects to the assessment objective helps keep the text cohesive and well-directed.&lt;/p&gt;

&lt;h2 id=&quot;look-for-references-and-standards&quot;&gt;Look for references and standards&lt;/h2&gt;

&lt;p&gt;Writing good reports is a skill developed through practice and observation. Reviewing well-structured security reports helps identify market standards, effective writing styles, and sound communication practices.&lt;/p&gt;

&lt;p&gt;Public reports, bug bounty programs, and &lt;a href=&quot;https://github.com/juliocesarfort/public-pentesting-reports&quot;&gt;repositories with real examples&lt;/a&gt; are valuable reference sources. Seeking inspiration does not mean copying templates, but understanding how other professionals communicate risk clearly and objectively.&lt;/p&gt;

&lt;h2 id=&quot;pay-attention-to-presentation&quot;&gt;Pay attention to presentation&lt;/h2&gt;

&lt;p&gt;Presentation directly affects a report’s credibility. Technically sound content that is poorly written or visually inconsistent conveys a lack of care. Consistent headings, proper spacing, and clear language make a noticeable difference.&lt;/p&gt;

&lt;p&gt;Grammatical errors and ambiguous sentences undermine the professional perception of the document. Regardless of the language used, careful review is essential. In the end, the report represents both the findings and the person who produced it.&lt;/p&gt;

&lt;h2 id=&quot;document-from-the-beginning&quot;&gt;Document from the beginning&lt;/h2&gt;

&lt;p&gt;A good report begins during the assessment, not after it ends. Notes taken from the start save time, prevent rework, and make future reviews easier. Recording commands, results, evidence, and observations allows the reasoning behind each finding to be reconstructed. Over time, it is natural to develop personal documentation models tailored to each professional’s workflow. The quality of the final report depends directly on the quality of this information.&lt;/p&gt;

&lt;h2 id=&quot;organization-tools-and-information-security&quot;&gt;Organization, tools, and information security&lt;/h2&gt;

&lt;p&gt;More important than the tool used is the process adopted. Information must be organized, accessible, and protected, especially since security assessments often involve sensitive data. Automating output capture, supplementing it with personal observations, and using screenshots judiciously help prevent the loss of relevant information. The ultimate goal is to ensure that data is clear, reliable, and easy to consult.&lt;/p&gt;

&lt;h2 id=&quot;the-importance-of-backups&quot;&gt;The importance of backups&lt;/h2&gt;

&lt;p&gt;Backups rarely receive attention until they become necessary. Lost documentation represents wasted time and, in some cases, direct impact on delivery. Keeping secure copies of documentation, relevant files, and environment snapshots should be part of routine practice. In information security, prevention almost always costs less than correction.&lt;/p&gt;

&lt;h2 id=&quot;the-importance-of-peer-review-and-external-perspectives&quot;&gt;The importance of peer review and external perspectives&lt;/h2&gt;

&lt;p&gt;No report should be considered final without review. Peer review improves technical quality, helps identify inconsistencies, and strengthens the narrative of the findings. Equally important is review by someone outside the assessment context. If that reader cannot understand the risk or the reasoning presented, it is likely that the client will not either.&lt;/p&gt;

&lt;p&gt;When human review is not available, language models can be used as support to identify grammatical issues, cohesion problems, and opportunities for improvement. They do not replace human reviewers, but can function as a second pair of eyes when used carefully.&lt;/p&gt;

&lt;h2 id=&quot;conceptual-tools&quot;&gt;Conceptual tools&lt;/h2&gt;

&lt;p&gt;Conceptual tools help organize thinking before and during writing. Models such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Five_Ws&quot;&gt;5W&lt;/a&gt; and 5W2H encourage reflection on purpose, context, scope, and method, helping avoid directionless reports.&lt;/p&gt;

&lt;p&gt;Structures such as “Problem, Cause, Impact, and Recommendation” help connect technical findings to real risks and practical actions. Threat scenario–based approaches make consequences easier to communicate, especially to non-technical audiences.&lt;/p&gt;

&lt;p&gt;Concepts like the &lt;a href=&quot;https://en.wikipedia.org/wiki/Inverted_pyramid_(journalism)&quot;&gt;inverted pyramid&lt;/a&gt; and the separation between fact, evidence, and interpretation improve clarity and credibility. Continually asking what the reader needs to understand or decide after each section helps keep the report focused and objective.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Security reports are communication tools. They connect technical analysis to decisions that affect people, processes, and businesses. Producing strong reports requires clarity of purpose, audience awareness, organized information, and attention to form. Reviews, sound documentation practices, and conceptual tools are not bureaucratic overhead, but quality mechanisms. A well-written report is not merely a record of findings; it is the bridge between discovery and action. Investing time in this stage is one of the most effective ways to generate real impact in information security.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/juliocesarfort/public-pentesting-reports&quot;&gt;https://github.com/juliocesarfort/public-pentesting-reports&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Inverted_pyramid_(journalism)&quot;&gt;https://en.wikipedia.org/wiki/Inverted_pyramid_(journalism)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Five_Ws&quot;&gt;https://en.wikipedia.org/wiki/Five_Ws&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Eight_disciplines_problem_solving&quot;&gt;https://en.wikipedia.org/wiki/Eight_disciplines_problem_solving&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Scientific_method&quot;&gt;https://en.wikipedia.org/wiki/Scientific_method&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Guides" />
    <summary type="html">After days or weeks of work, you have identified relevant security issues, gained a solid understanding of the assessed environment’s attack surface, and gathered sufficient evidence to demonstrate real business risks. From a technical standpoint, the assessment is complete. What remains is to turn that knowledge into a report that is clear, actionable, and easy to understand.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Strengthening the Brazilian cybersecurity community through education</title>
    <link href="https://blog.lesis.lat/community/2026/01/14/commitiment-to-strengthening-community-en.html" rel="alternate" type="text/html" title="Strengthening the Brazilian cybersecurity community through education" />
    <link href="https://blog.lesis.lat/assets/audio/strengthening-community/en.mp3" rel="enclosure" type="audio/mpeg" length="980994" />
    <published>2026-01-14T19:13:00-03:00</published>
    <updated>2026-01-14T19:13:00-03:00</updated>
    <id>https://blog.lesis.lat/community/2026/01/14/commitiment-to-strengthening-community-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/community/2026/01/14/commitiment-to-strengthening-community-en.html">&lt;p&gt;In 2026, LESIS will take another concrete step in its commitment to the sustainable development of the technology and information security ecosystem in Brazil. We will make a financial donation to the NGO &lt;a href=&quot;https://mentebinaria.com.br/&quot;&gt;Mente Binária&lt;/a&gt;, an organization that works directly on training new professionals for the cybersecurity field.&lt;/p&gt;

&lt;p&gt;We believe that information security is not built solely with technology, but with well-prepared people, access to knowledge, and real opportunities for professional growth. This is where Mente Binária’s work becomes essential: by investing in accessible, high-quality technical education, the organization helps transform individual trajectories, impacting families, communities, and ultimately the country itself.&lt;/p&gt;

&lt;p&gt;Training new professionals in security strengthens the entire value chain — it helps reduce the talent gap in the market, raises the technical standard of the industry, and contributes to a safer, more resilient digital environment prepared for current and future challenges. Each trained professional represents not just a new career, but a collective advance for the information security sector as a whole.&lt;/p&gt;

&lt;p&gt;Our financial contribution is, in itself, a single action. However, it carries a broader conviction: when the right resources reach the right people, impact multiplies. Small actions, when taken consistently and aligned with a clear purpose, build lasting change.&lt;/p&gt;

&lt;p&gt;As an institution, LESIS understands that its role goes beyond commercial activity. We have a responsibility to contribute positively to the ecosystem in which we operate, supporting initiatives that generate social, technical, and human value.
We will continue to seek ways to support projects that believe in education as a driver of transformation. Because even when each individual action seems small, it is through these steps that great impact is built.&lt;/p&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Community" />
    <summary type="html">In 2026, LESIS will take another concrete step in its commitment to the sustainable development of the technology and information security ecosystem in Brazil. We will make a financial donation to the NGO Mente Binária, an organization that works directly on training new professionals for the cybersecurity field.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Why we sponsor information security events</title>
    <link href="https://blog.lesis.lat/community/2025/11/30/sposoring-events-en.html" rel="alternate" type="text/html" title="Why we sponsor information security events" />
    <link href="https://blog.lesis.lat/assets/audio/sponsoring-events/en.mp3" rel="enclosure" type="audio/mpeg" length="1427556" />
    <published>2025-11-30T09:33:00-03:00</published>
    <updated>2025-11-30T09:33:00-03:00</updated>
    <id>https://blog.lesis.lat/community/2025/11/30/sposoring-events-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/community/2025/11/30/sposoring-events-en.html">&lt;p&gt;The year 2025 has been a milestone for LESIS. Between projects, research, and the growth of our presence in the market, we have also taken on a commitment we consider essential: &lt;strong&gt;to directly contribute to and with the Information Security community&lt;/strong&gt;. We believe that innovation is born from people connecting, from the continuous exchange of knowledge, and from the collective construction of solutions that make the digital world safer. That’s why supporting events across the country has become a priority and a concrete way to help the industry evolve.&lt;/p&gt;

&lt;p&gt;Throughout this year, we have financially sponsored and participated in initiatives such as &lt;a href=&quot;https://www.linkedin.com/posts/lesis-lat_ever-thought-about-spending-a-full-day-diving-activity-7361743511337017344-qYrN/&quot;&gt;Hacking na Web Day RJ&lt;/a&gt;, in Rio de Janeiro; &lt;a href=&quot;https://www.linkedin.com/posts/lesis-lat_xibésec-is-back-for-its-third-edition-bringing-activity-7364280260671987714-nAGf/&quot;&gt;XibéSec&lt;/a&gt;, in Belém; &lt;a href=&quot;https://www.instagram.com/lesis.lat/p/DRMt3HTkaKl/&quot;&gt;RioCyberSec&lt;/a&gt;, also in Rio; FortalSec, in &lt;a href=&quot;https://www.linkedin.com/posts/lesis-lat_during-the-month-of-february-two-researchers-activity-7299858979961118720-jJwY/&quot;&gt;Fortaleza&lt;/a&gt;; and &lt;a href=&quot;https://www.linkedin.com/posts/lesis-lat_hackincariri-ciberseguranaexa-tecnologia-activity-7287745352982519809-fVA2/&quot;&gt;Hack in Cariri&lt;/a&gt;, in Juazeiro do Norte. Each of these events has its own identity and audience: some with a strong academic focus, others more industry-driven, some already consolidated and large-scale, and others still expanding. For us, this diversity is precisely what makes the community so rich — and supporting different profiles is a way to ensure that knowledge reaches farther and farther. Beyond sponsoring, we also made sure to be present as participants in major events such as &lt;a href=&quot;https://www.linkedin.com/posts/lesis-lat_lesis-is-proud-to-join-the-25th-edition-of-activity-7359220025188139008-KKYG/&quot;&gt;SBSEG&lt;/a&gt;, &lt;a href=&quot;https://websummit.com/&quot;&gt;Web Summit&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/posts/htrgouvea_bsidesrj-foi-incr%C3%ADvel-obrigado-p-quem-separou-activity-7309547965197565952-R3Q7&quot;&gt;BSides RJ&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These are spaces where the most relevant cybersecurity discussions thrive, new connections emerge, and strategic partnerships are built. Being close to those conversations is essential both for LESIS’ growth and for our ability to effectively contribute to advancements in the field.&lt;/p&gt;

&lt;p&gt;Naturally, as a company, part of this investment returns in the form of brand positioning, networking, and business opportunities. However, that is not what drives us. Our main motivation is to give back to the community everything it represents to us: shared knowledge, constant inspiration, and the development of professionals who are building a safer future for everyone. We want to take part in the foundation that sustains this ecosystem, fostering new talent and strengthening initiatives that make a difference for those entering the field and for those who have already walked a long journey.&lt;/p&gt;

&lt;p&gt;We also make a point of spreading our support across different regions of Brazil. While some hubs, like São Paulo, already benefit from high investment volumes and frequent events, other areas still face limited initiatives and opportunities. Supporting gatherings in the North and Northeast, for example, is a way to help create a more balanced landscape, where talent is not restricted by geography.&lt;/p&gt;

&lt;p&gt;This movement is just beginning. We remain open to collaborating with those who share the same vision: a strong, diverse, accessible, and constantly evolving community.&lt;/p&gt;

&lt;p&gt;If you organize an event and believe in the impact of education, knowledge exchange, and the development of information security, we want to help build this future by your side.&lt;/p&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Community" />
    <summary type="html">The year 2025 has been a milestone for LESIS. Between projects, research, and the growth of our presence in the market, we have also taken on a commitment we consider essential: to directly contribute to and with the Information Security community. We believe that innovation is born from people connecting, from the continuous exchange of knowledge, and from the collective construction of solutions that make the digital world safer. That’s why supporting events across the country has become a priority and a concrete way to help the industry evolve.</summary>
  </entry>
  <entry xml:lang="en">
    <title type="html">Money for nothing, discounts for free: rocking the gift card loop</title>
    <link href="https://blog.lesis.lat/vulnerability/2025/10/04/rocking-gift-card-en.html" rel="alternate" type="text/html" title="Money for nothing, discounts for free: rocking the gift card loop" />
    <link href="https://blog.lesis.lat/assets/audio/gift-card-loop/en.mp3" rel="enclosure" type="audio/mpeg" length="2496301" />
    <published>2025-10-04T13:29:00-03:00</published>
    <updated>2025-10-04T13:29:00-03:00</updated>
    <id>https://blog.lesis.lat/vulnerability/2025/10/04/rocking-gift-card-en</id>
    <content type="html" xml:base="https://blog.lesis.lat/vulnerability/2025/10/04/rocking-gift-card-en.html">&lt;p&gt;From time to time, the opportunity arises to conduct vulnerability research on e-commerce platforms. Although the context is similar, each opportunity is unique, as there are always peculiarities in every application. In this post, I aim to demonstrate a case involving gift cards that I find particularly interesting.&lt;/p&gt;

&lt;p&gt;Websites that sell any type of product have an enormous level of complexity in their logic implementations, and this complexity often makes it difficult to ensure that all flows contain the appropriate security measures. In this post, we will specifically address two items frequently present in e-commerce platforms: gift cards and discount coupons, in a case study where primitives were found that allowed the exploitation of a vulnerability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gift cards:&lt;/strong&gt; whether virtual or physical, a gift card is a type of prepaid card that customers can purchase for themselves or to give to others. It has a monetary value associated with it, which can be used for purchases on the website, allowing the recipient to choose the products or services they want until the gift card balance is fully spent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discount coupon:&lt;/strong&gt; this is a code, usually alphanumeric, provided to customers to reduce the purchase price of products or services. When applied during checkout, the customer receives a discount or specific benefit, such as a percentage off or free shipping, making the purchase more advantageous.&lt;/p&gt;

&lt;h2 id=&quot;rocking-the-gift-card-loop&quot;&gt;Rocking the gift card loop&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/ecommerce-giftcard/product-list.webp&quot; alt=&quot;Gift card listed for sale in the e-commerce product catalog&quot; width=&quot;1354&quot; height=&quot;662&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Figure 1: Listing of a Gift Card for sale&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During a testing activity cycle, I identified the following scenario: a site offers the option to purchase gift cards and, at the same time, allows the use of discount coupons in that very purchase. For example, when purchasing a gift card worth R$150.00, it is possible to apply a discount coupon called “15OFF,” which grants a R$15 discount. As a result, the amount paid is only R$135.00 for a gift card that still has a “purchasing power” of R$150.00.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/publications/ecommerce-giftcard/checkout.webp&quot; alt=&quot;Checkout screen buying a gift card with a discount coupon applied&quot; width=&quot;1310&quot; height=&quot;1112&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Figure 2: Buying a Gift Card with a discount coupon in an e-commerce&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue in this scenario is that the gift card can then be used to purchase another gift card, while once again applying the discount coupon. Thus, in the next transaction, it is possible to acquire a R$150.00 gift card using a gift card that cost only R$135.00, while again applying the R$15.00 discount coupon.&lt;/p&gt;

&lt;p&gt;This creates a pattern: each time this flow is repeated, R$15.00 of additional balance is illegitimately generated. In practical terms, with every transaction the user increases their available credit without making any additional financial outlay.&lt;/p&gt;

&lt;p&gt;To illustrate, if this process is repeated across 10 consecutive cycles, the initial R$150.00 credit evolves into R$300.00, representing a 100% increase in balance with no financial counterpart. Such a situation characterizes a systemic fraud scenario, as it enables the creation of fictitious credit within the platform.&lt;/p&gt;

&lt;p&gt;Additionally, it is important to note that each transaction carried out is subject to operational fees charged by payment intermediaries. Thus, beyond the financial loss from the illegitimate generation of balance, the recurring processing of transactions increases operational costs, amplifying the negative financial impact on the platform.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The vulnerability identified stems from business logic flaws, and its mitigation requires structural adjustments to the purchase flow. The first and most direct measure is to restrict the use of gift cards as a payment method for acquiring new gift cards. This simple rule eliminates the possibility of chaining transactions that result in the artificial generation of balance.&lt;/p&gt;

&lt;p&gt;Complementarily, it is advisable to establish specific policies for discount coupons, preventing them from being applied to gift card purchases. This practice is common in the market and aims to preserve the financial integrity of the system, since gift cards function in practice as stored-value instruments and should not be subject to promotions that reduce their acquisition cost.&lt;/p&gt;

&lt;p&gt;Furthermore, implementing fraud prevention and detection mechanisms is highly recommended. The use of an antifraud engine integrated into the checkout process allows the identification of suspicious patterns, such as multiple transactions of apparently low risk that in practice reveal attempts at systemic exploitation. Such controls, combined with continuous monitoring and usage limitation policies (e.g., maximum gift card purchase value per customer within a given period), strengthen the platform’s resilience against similar abuses.&lt;/p&gt;

&lt;p&gt;Taken together, these measures represent a multilayered mitigation approach, reducing the company’s exposure to financial fraud risk and ensuring greater reliability.&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://soroush.me/downloadable/common-security-issues-in-financially-orientated-web-applications.pdf&quot;&gt;&lt;em&gt;Common Security Issues in Financially- Oriented Web Applications – NCC Group&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://datadome.co/threats/gift-card-fraud-prevention/&quot;&gt;&lt;em&gt;Gift Card Fraud Prevention Methods &amp;amp; Solutions&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
    <author>
      <name>Heitor Gouvêa</name>
    </author>
    <category term="Vulnerability" />
    <summary type="html">From time to time, the opportunity arises to conduct vulnerability research on e-commerce platforms. Although the context is similar, each opportunity is unique, as there are always peculiarities in every application. In this post, I aim to demonstrate a case involving gift cards that I find particularly interesting.</summary>
  </entry>
</feed>
