Skip to main content

30 posts tagged with "Application security"

Code-level security beyond Salesforce, with Vulkro Core.

View All Tags

Agent tools are entry points, so we scan them like routes

· 9 min read
Vulkro
Security research

A web route has an obvious attacker: whoever sends the request. A tool in an MCP server or an AI agent looks different. Its arguments are filled in by a model, the model is working for your user, and the code reads as if the caller were trusted. That is the mistake. The model picks the arguments from everything in its context, and part of that context can be written by someone else.

So we stopped treating tools as internal functions. Since Vulkro 0.30.0, MCP server tools and AI agent tools are entry points, the same as HTTP routes: a tool whose argument reaches a shell, a query or a file is reported, with the path from the argument to the call.

Proven, unproven, not checked: three honest answers

· 8 min read
Vulkro
Security research

Every finding a scanner reports is a claim about your code. Some claims come with evidence: the scanner followed a value from the request, through two helpers, into a SQL string, and can show every step. Others are a pattern that looked suspicious. Others are questions the scanner raised but could not answer from the source at all.

Most scan output presents all three the same way: a rule, a line and a severity. The reader has to work out which kind each row is, usually by opening the file and tracing the code by hand. Multiply that by a few hundred rows and it becomes the reason teams stop reading the results.

Vulkro gives every finding one of three labels instead. Proven, unproven or not checked. This post explains what each one means, what changed in how they drive severity, and why an empty proof never means the code is safe.

Broken access control is still number one, and 2025 showed why

· 8 min read
Vulkro
Security research

The most damaging bug in web software is also the least interesting to describe. A request asks for record 1041. The server checks that you are logged in, loads record 1041 and returns it. It never asks whether record 1041 is yours. Change the number and you get your neighbour's.

That is broken object-level authorization, also called an insecure direct object reference or IDOR. The OWASP Top 10:2025 keeps broken access control at number one, and says that "100% of the applications tested were found to have some form of broken access control." Among the weaknesses it maps there is CWE-639, authorization bypass through a user-controlled key: the changed number.

The sign-in that never checks the signature

· 9 min read
Vulkro
Security research

A JSON Web Token has three parts: a header, a set of claims and a signature. The claims are what the application wants to know (who the caller is, what roles they hold, when the token expires). The signature is the only part that makes those claims worth believing. Without it, a token is a base64 string anyone can write by hand.

Most JWT libraries ship two functions that look alike. One verifies the signature and returns the claims. The other skips the check and returns the claims anyway, because there are honest reasons to peek inside a token: reading the kid header to choose a key, or showing an expiry time in a UI. When the second function ends up in the authentication middleware, the application still has a sign-in screen, still issues tokens and still rejects requests without one. It just accepts any token at all.

Securing AI coding agents

· 8 min read
Vulkro
Security research

An AI coding agent can write a new endpoint, its handler and its database query in the time it takes to read this paragraph. It will compile. It will probably pass the tests it wrote for it. Whether it checks that the caller owns the record it returns is a separate question, and nobody asked it.

That gap is the security problem with AI coding agents. Not that they write worse code than people, but that they write a lot of it, quickly, and the review that used to happen while a person typed it no longer happens at all.

The missing tenant column

· 9 min read
Vulkro
Security research

A multi-tenant SaaS application keeps every customer's data in the same database and promises each of them they will only ever see their own. That promise is usually kept by one line repeated in hundreds of places: WHERE tenant_id = ?. Every query that remembers it is correct. The one that forgets shows a customer another company's records.

Cross-tenant leaks are the business-to-business version of the IDOR bugs we wrote about earlier. The difference is the blast radius. An IDOR exposes one user's record to another user. A cross-tenant leak exposes one customer's company to another customer's staff, and tends to end with an incident notice sent to every affected account.

Checking another scanner's findings against a proof

· 8 min read
Vulkro
Security research

Most teams that care about security already run more than one tool. A query-based analyser in CI, a linter with security rules, a dependency checker, and lately an AI reviewer that comments on pull requests. Each one writes findings, and someone has to decide which of them are real.

That decision is the expensive part. Producing a finding is cheap. Confirming that untrusted input actually reaches the flagged line, with nothing in between that makes it safe, takes a person reading code. When a tool reports two hundred results, that reading is what stalls.

vulkro verify is built for that step. It takes another tool's results as a SARIF file, checks each one against Vulkro's own analysis of the same repository, and tells you which ones it can prove, which ones it looked at and cannot, and which ones it did not look at.

Severity should depend on who can reach the code

· 8 min read
Vulkro
Security research

Here are two SQL injections in the same Express application. One is on a public search endpoint that anyone on the internet can call. The other is on a reporting screen that only administrators can open, behind single sign-on. The vulnerable line is almost identical in both.

Many scanners rate them the same, because severity is attached to the rule: "SQL injection" is Critical wherever it appears. The result is a list where everything at the top looks equally urgent, and the team has to redo the risk assessment by hand for every row.

They are not the same risk. Both should be fixed, but one can be exploited by anyone right now and the other needs an attacker who already holds an administrator account. Severity that cannot tell them apart is not telling you much.

Unsigned webhooks and request bodies written to the database

· 8 min read
Vulkro
Security research

Some of the most damaging authorization bugs do not look like authorization code at all. They look like plumbing. A webhook route that receives an event and updates an order. An update handler that takes the request body and saves it. Neither has an obvious place for a permission check, which is exactly why the check is often missing.

Vulkro 0.30.0 added a set of authorization checks to its scan, and two of them cover this plumbing: webhooks that write data without verifying who sent the request, and request bodies written straight to the database. This post walks through both, with the vulnerable shape, the fix and what the scanner reports.

Deterministic analysis vs asking a model

· 8 min read
Vulkro
Security research

"Paste the repo into the model and ask it to find the security bugs" has become a common first attempt at AI-assisted security review. It is easy to try, and the first run is often impressive: the model names a plausible injection, explains it well, and suggests a fix.

The trouble starts on the second run. And the third. And when somebody asks what it did not look at.

Vibe coding security: the bugs AI-built apps ship with

· 9 min read
Vulkro
Security research

Vibe coding security is not a new kind of security. The apps that come out of AI app builders and AI coding agents fail in the same ways hand-written apps do. What changes is the rate. An app that would have taken a team a month now takes an afternoon, and nobody on that afternoon was thinking about who else can call the endpoints.

The bugs are also surprisingly consistent. Pages that look locked but are not. A database queried straight from the browser with nothing on the server side deciding who sees which row. A secret key in the JavaScript bundle. A token that is read but never verified. A record fetched by id with no check on whose it is.

This post goes through those five, with the research behind them, then gives a pre-ship checklist and the commands to check an app before it goes live.

Fixes you can trust, because each one is checked

· 8 min read
Vulkro
Security research

Every suggested fix makes two claims. The first is that it removes the problem. The second is that it does not add a new one. Most fixes, whether they come from a colleague, a linter or an AI assistant, arrive with neither claim checked. Someone reads the diff, it looks reasonable, and it is merged.

For ordinary bugs the test suite catches some of the bad ones. Security bugs are different, because the tests rarely exercise the hostile input. A fix that escapes the wrong variable, or moves string concatenation one function further away, passes every test the old code passed.

Since Vulkro 0.30.0, vulkro fix --verify checks both claims before you see the patch. This post is about why that matters and exactly what the check does and does not prove.