The last couple of days at work have been intense. On Tuesday details of a new PHP vulnerability started leaking online, and by Wednesday afternoon the official patches were out. Everything else on the board stopped. For two days the only questions were: where do we run PHP, which version is it, and is it patched yet?
The bug has a name, PHP2Shell, and it is one of the most serious problems the web has seen in years.
What is PHP?
PHP is a programming language that runs on the server. When you open a web page, PHP builds the HTML before it reaches your browser. It was created by Rasmus Lerdorf in 1994 and is still everywhere. WordPress, Drupal, Joomla, Laravel, Symfony, Magento, and MediaWiki (the software behind Wikipedia) are all written in it.
The numbers are impressive. According to these PHP statistics from September 2026:
PHP runs on 70.2% of all websites whose server-side language is known (W3Techs).
Among PHP sites, 63.4% use PHP 8, 28.7% still use PHP 7 and 7.9% are still on PHP 5.
Packagist, PHP’s package registry, lists 466,248 packages with almost 197 billion installs since 2012.
About 19% of professional developers worked extensively with PHP last year (Stack Overflow 2025), and 64% of PHP developers use Laravel (JetBrains 2025).
PHP may not be fashionable, but it powers a huge part of the internet. That is exactly why a bug in PHP itself is such a big deal.
CVE-2026-109618: when PHP runs out of memory, for example during a huge file upload, it can be interrupted while its internal request data is only half built. The cleanup step then treats that broken data as valid.
CVE-2026-109619: some internal state survives after its memory has been freed. The next request on the same worker can use that stale state, and the upload progress feature is one way to reach it.
Both lead to remote code execution, which means an attacker can run their own commands on your server. Here is why it was so bad:
No login needed. A single anonymous request is enough to attack, and it happens before any application code runs. It doesn’t matter how secure your own code is.
It’s in PHP itself, not in a plugin or framework, so it affected PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed, from PHP 5.4 all the way to 8.5.
Attackers were early. Amazon saw an exploitation attempt on October 17, four days before the public disclosure. The details then leaked on October 20, a day ahead of schedule.
Old versions don’t get proper fixes. PHP 7.4, 8.0 and 8.1 only received source patches, and nothing older got anything. With almost 37% of PHP sites still on PHP 7 or 5, many servers can’t simply “update”.
As a precaution, government sites in Europe and Canada, many Wikimedia services and even the Internet Archive went offline for a while. Interestingly, the bug was found with the help of AI models. One of the out-of-memory problems had been spotted back in 2024, but without a working exploit it was never fixed.
Patch everything, now
So what did we developers do? We patched as fast as we could. The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
If you run anything with PHP, even a small blog on shared hosting, check today that it’s on a patched version.
Lets see what the next week will bring in this new crazy world of LLM driven security research and exploitation.
Yesterday a hacker compromised the account of the lead developer of axios — the npm package with around 100 million weekly downloads — and published two malicious versions that included a remote access Trojan targeting macOS, Windows and Linux.
The malicious code was pulled in through a staged dependency called "plain-crypto-js" and was designed to self-destruct after execution. It was only live for about three hours, but that was enough: security firm Huntress reported the first infection on a monitored endpoint just 89 seconds after the compromised version was published.
This got me thinking: do we actually still need axios? The original reason it became so popular was that it gave you a clean, consistent API for making HTTP requests that worked the same way in the browser and in Node.js. But Node.js has had native fetch since version 18, and it's been stable for a while now. The browser has had it for years. So the problem axios originally solved is basically gone.
For my typical axios usage, I wrote a simple fetch wrapper called fetchios that mirrors the axios API — same .get(), .post(), .create(), interceptors and all. It works as a drop-in module so I don't have to change the code everywhere, just copy the file into utils, swap the import, and remove axios from my dependencies.
Every dependency you add is a potential attack surface — and this incident is a perfect reminder of that. Maybe it's time to stop running npm install axios by inertia and check what the platform already gives you.
I run a small VPS for some personal projects, and I often check the SSH logs to see who’s trying to break in. I have the standard setup: key-based authentication, password login disabled, a non-default SSH port, and fail2ban configured to block IPs after a few failed attempts. But that doesn’t stop the bots from trying.
This week, I decided to do a little analysis of the SSH logs to see where the attacks are coming from, and the results are not surprising but still interesting.
Nearly 31,000 failed login attempts from the top 20 IPs alone. Russia takes the lead with 45% of attacks (20,742 attempts), followed by the US at 30% (8,163 attempts). One particularly persistent Russian IP tried over 9,600 times. Points for determination, I guess?
The geographic diversity is almost impressive: Russia, US, China, Brazil, and South Korea are all represented. It’s like a World Cup of “please let me into your server.”
This is just life when you have an SSH server exposed to the internet. I decided to add some additional security measures this week after seeing the data, but I wanted to share the conclusion of this analysis as a public service to anyone else who might be running a VPS with SSH access.
TL;DR: This is going to be a long post about the several iterations this blog has gone through, explaining how I recently finished migrating it to Astro.
Since its creation in January 2016 as a new year’s resolution, this blog has undergone several iterations. Recently, I finished migrating it to Astro, a new framework that has gained a lot of popularity and secured a top position in the latest State of JS survey. If you’re not familiar with Astro, it’s definitely worth checking out. The objective of this post is to describe the rationale behind choosing Astro for this project and my overall experience with the migration.
Astro on the map
Last January when the “State of JS” survey was released, I was eager to read it as usual. As I explained in a post some months ago, this online survey has been running since 2016, collecting and analyzing data from JS developers, and it is definitely a valuable tool for detecting current trends in the JS ecosystem and identifying upcoming ones.
One of the points that caught my attention in this issue, in the Rendering Frameworks section, was the popularity of “Astro”. In just one year, this young project has doubled the interest expressed by developers, so I decided to investigate a little more why it was so popular.
In the official documentation Astro defines itself as “an all-in-one web framework for building fast, content-focused websites.” But one of the key selling points is in the features section: “UI-agnostic: Supports React, Preact, Svelte, Vue, Solid, Lit and more.”. Perfect! That was exactly what I was looking for!
Evolution and why Astro
As previously mentioned, I started this blog in 2016 as a New Year’s resolution. Back then, I opted to use Google Spreadsheets as a “poor man’s” CMS due to its convenience in configuring various blog fields and preserving blog content, while also obtaining a nice JSON response. However, as time passed, the API response underwent changes, prompting me to create a small wrapper to align with the previous response format.
On the other hand, like many other developers around the world, I had the opportunity to work on a couple of projects with the amazing next.js framework, and while I was browsing some of the documentation and examples, I stumbled upon the very useful gray matter npm package.
To put it simply, this package enables you to consolidate both data and metadata into a single text file by utilizing a “matter” section at the beginning of the file, which can then be transformed into a JSON object.
As I was already crafting my posts in markdown format and generating JSON file outputs in my previous Spreadsheets version, it was a straightforward task for me to migrate all of my posts into separate markdown files using this package, and then I could combine these MD files into a single JSON file to be used as the “source” of this blog.
This was my approach for a period of time: I continued to utilize the same node.js express application, but with an alternate “static” JSON source that I could regenerate whenever necessary. However, given that this blog is essentially a static website, I had always entertained the notion of transitioning to a fully static site.
When I came across the Astro documentation, I was immediately convinced, because it aligned seamlessly with the problems I was attempting to address:
To serve this blog fully static.
To make the code more maintainable.
To use a modern framework.
To keep the existing look and feel (and reuse the CSS styles as much as possible).
To reuse some of the existing vanilla JavaScript code.
To add some nice reusable React components I had used in some other projects.
To keep the nice JSX syntax I'm used to working with in React and Next.js.
Experience
Having spent a week on this migration, my general perception as a developer is highly favorable. Throughout the project, I found Astro to be remarkably versatile and capable of supporting the reuse of existing code, adding custom styles, and allowing component integration from other projects.
It is clear that the project is moving fast because some of the documentation I found on the internet was already outdated, but I found the official documentation to be of high quality overall. Most of my questions were promptly resolved through the official site without any problem.
I jump-started the project using the recommended npm create astro and adapted the generated template to my needs.
In the Project Structure Section the official documentation describes the purpose of the different parts of the application including Pages, Layouts and Components. I followed this recommended structure with a couple of additions:
I set up an additional `content` folder outside the src folder to put all my existing (and future new) `.md` blog posts.
I created a `utils` folder to place common utilities used across the different components and Astro pages.
Not including every single file, my final project looks more or less like this tree:
If you are familiar with next.js, the routing pattern associated with the pages directory will probably look familiar. On the other hand, Astro defines layouts as “Astro components used to provide a reusable UI structure, such as a page template.”
As discussed earlier, this migration was a really nice experience, and I would probably use Astro again for a static site!
State of JavaScript is an online survey that, since 2016, has collected and analyzed data from JS developers to detect current trends in the ecosystem and identify upcoming ones. A couple of days ago the 2022 edition was released, and the results are quite interesting.
React continues to be very strong, both in number of users and in its retention ratio (the percentage of users who would use a library again). I have been lucky to work with this library in recent years, and despite its drawbacks, I can understand why it is still quite appealing.
It is also interesting to see what happens with the other two big ones (Angular and Vue.js):
While Vue.js has similar retention levels to React, its number of users has not yet reached the same levels. Angular’s interest and retention levels are really low. This is surprising, given that it is still one of the most sought-after frameworks in the industry.
Normally I don't write about programming topics on this blog, since I dedicate it more to pictures, maps, and charts, but a couple of days ago I was explaining to a colleague at work some parts of an internal tool in which we use React and functional programming.
And it is true that if you haven't had experience with JavaScript's higher-order functions map, filter & reduce, they might appear daunting at the beginning. But once you get used to them, you never look back! They are extremely useful for manipulating arrays and arrays of objects.
So the objective of this post is to explain all three of them using a simple example.
Data
Let’s create a quick array of objects with the top five movies of all time according to IMDb ratings:
const top5Movies = [ { "title": "The Shawshank Redemption", "rank": "1", "id": "tt0111161", "imdbrating": "9.3", "year": "1994", "genre": "Drama", "director": "Frank Darabont", "actors": "Tim Robbins, Morgan Freeman, Bob Gunton", "language": "English", "country": "United States" }, { "title": "The Godfather", "rank": "2", "id": "tt0068646", "imdbrating": "9.2", "year": "1972", "genre": "Crime, Drama", "director": "Francis Ford Coppola", "actors": "Marlon Brando, Al Pacino, James Caan", "language": "English, Italian, Latin", "country": "United States" }, { "title": "The Godfather: Part II", "rank": "3", "id": "tt0071562", "imdbrating": "9.0", "year": "1974", "genre": "Crime, Drama", "director": "Francis Ford Coppola, Mario Puzzo", "actors": "Al Pacino, Robert De Niro, Robert Duvall", "language": "English, Italian, Spanish, Latin, Sicilian", "country": "United States" }, { "title": "Pulp Fiction", "rank": "4", "id": "tt0110912", "imdbrating": "8.9", "year": "1994", "genre": "Crime, Drama", "director": "Quentin Tarantino", "actors": "John Travolta, Uma Thurman, Samuel L. Jackson", "language": "English, Spanish, French", "country": "United States" }, { "title": "The Good, the Bad and the Ugly", "rank": "5", "id": "tt0060196", "imdbrating": "8.8", "year": "1966", "genre": "Adveture, Western", "director": "Sergio Leone", "actors": "Clint Eastwood, Eli Wallach, Lee Van Cleef", "language": "Italian, English", "country": "Italy, Spain, West Germany" }]
Map
Let’s say a friend says to you, ah, great dataset, but could you give me a list with just the titles of those movies? In this case map is your friend :). This method loops over an array and runs a function on each element of the array, returning the modified value as a new element of a new array.
In our case, we want to loop over this top5Movies array and just extract the titles:
So with this simple statement we loop over each “movie” item (an object), apply a simple function (with arrow notation) that returns the title (implicit return), and assign the results to this new titles array. If we look at the contents of this array, our titles should be there:
> titles[ 'The Shawshank Redemption', 'The Godfather', 'The Godfather: Part II', 'Pulp Fiction', 'The Good, the Bad and the Ugly']
If you want to better understand the concept, think about it without arrow notation:
top5Movies.map( function (movie) { return movie.title })
As you can see, you are applying a function on each iteration and returning the value.
Please also note that the map method will always create a new array without mutating the original one.
Filter
Now let’s say another friend asks you if you can give them a copy of this array of objects, but including only the movies in which Spanish is spoken. This is where the filter operation can be quite useful. This method iterates over the source array, but instead of returning a modified copy of your array element, it just tests the condition defined in the function and returns a copy of the original element if the condition is met.
So, for this request, we will check if the field “language” includes the string “Spanish”:
const topMoviesWithSpanish = top5Movies.filter( movie => movie.language.includes("Spanish"))
The newly created topMoviesWithSpanish array will only include:
[ { "title": "The Godfather: Part II", "rank": "3", "id": "tt0071562", "imdbrating": "9.0", "year": "1974", "genre": "Crime, Drama", "director": "Francis Ford Coppola, Mario Puzzo", "actors": "Al Pacino, Robert De Niro, Robert Duvall", "language": "English, Italian, Spanish, Latin, Sicilian", "country": "United States" }, { "title": "Pulp Fiction", "rank": "4", "id": "tt0110912", "imdbrating": "8.9", "year": "1994", "genre": "Crime, Drama", "director": "Quentin Tarantino", "actors": "John Travolta, Uma Thurman, Samuel L. Jackson", "language": "English, Spanish, French", "country": "United States" }]
Since those are the movies that include “Spanish” in the language field. As was the case with map, filter will create a new copy of the array so the original is not mutated.
Reduce
Finally, another friend asks you if it is possible to get the average rating of those top 5 movies. So you say sure, take the map function you already learned, and tell them: here are the ratings:
And your friend says: well, thank you, but what I asked for is the “average” of those.
You start to think about it 🤔, and realise you need to iterate over each rating but instead of changing it, you need to add it to the next one and so on, and once you have the final sum, you can divide it by the number of ratings.
This process of “aggregating” values is exactly what the reduce method does; it iterates over each element and applies a function you define with the item and the aggregator.
Let’s take those ratings and aggregate (sum) them together:
As you can see, your ratings array was “reduced” to a single value that’s the sum of the ratings.
Using this principle, we can chain our map function that extracts the rating with this reduce one, and by dividing by the number of items, we can give our friend the average:
Now, one of the most powerful features of this reduce higher-order function is that it allows you to create an object from an array.
In that case, the reduce function takes two arguments: a starting value and a function. The starting value is the initial value of the accumulator, which is the object that will be created. The function is used to iterate over the array and add each element to the accumulator. The function takes two arguments: the accumulator and the current element.
So let’s go back to our movies example. Let’s say we want to convert that top5Movies array into an object, keyed by the IMDb id, so it’s easier to get a movie based on this id.
So let’s do it in two steps. First, let’s create the template that we will use for writing our reduce function:
Obviously this returns an empty object, because it’s iterating over our top5Movies but always returning the empty accumulator (acc) that we initialized at the end of this reduce function ({}). But if now we just add:
Some weeks ago, when I finished work, I was about to leave the building and saw on the wall a sheet of paper with information about a Vue.js meetup here in Berlin. I had never heard about Vue.js, and the more I read, the more interested I became in this front-end JavaScript framework.
Google Trends shows that Vue.js has been the most popular framework in the last 12 months. Vue.js is self-described as "a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable". I gave it a go, and it's quite interesting.
I have been using Python for many years and I recognize how powerful it is for data science with libraries like Pandas and Scikit-Learn. In a previous role, I had the opportunity to use R, but I haven't touched it in a while. I was curious to see the interest in R vs Python on Google Trends, and you can see the graph above plotted with this data source.
Last weekend I finished converting the FX calculator I developed during my career break to React.js. I have briefly touched some React code at work, but I wanted to get my hands dirty and better understand the advantages and challenges of this library.
Today I was also checking some interesting [Google Trends data](https://t.co/aCOxxTOhUo) about the most popular JavaScript frameworks in terms of search interest, and React is definitely winning the interest war.
I was discussing it with a colleague a couple of days ago: Facebook's React is definitely here to stay. Look at the graphs above showing the increase in Wikipedia page views during the last year and the growing trend on Stack Overflow.
In this new year, one of my resolutions was to dust off my social accounts and update them regularly.
I also thought it would be interesting to use my personal website to keep a blog with the most noteworthy updates from those social profiles, or to write about anything that comes to mind. However, I didn't want to spend a lot of time setting up yet another blog using WordPress or something similar. It was an interesting experience when I did it for my career break, but this time around I wanted a simpler solution.
Based on my recent experience writing back and forth to Google Spreadsheets, I decided to use it as a super simple CMS. Through this approach, I can write posts directly in the spreadsheet interface and include the HTML tags I need, such as this bold or this italic, as well as links to various media elements I might want to include in the posts.
Once I'm happy with a particular post, I can export its contents as a JSON file, which I then parse and display in my application. In the image on the right, you can see how my CMS looks :). I essentially used the request Node module to get the JSON string and then parse it within my application:
What I like about this approach is that I can iterate over the JSON object returned by Google Spreadsheets and extract each value I’m interested in, then push them to my local JSON object.
When I render the data on the frontend, I can access those fields quite easily. I’ll probably write a detailed post later on about how the process works, but for now, I’m happy with the results.
Note: When I wrote this original post, I was using Google Spreadsheets. Nowadays, I’ve completely switched to a Markdown-only approach.