Safeguard
Application Security

Fingerprinting AI-Built Web Apps From the Outside

A DAST scan has no repository and no commit history — only what the server sends a browser. That is enough to identify the builder that generated an app, and nowhere near enough to name the model.

Karan Patel
Security Engineer
5 min read

Static analysis can read a repository's commit history and find out which assistant wrote what. Dynamic scanning gets none of that. A DAST engine sees a hostname, some HTTP responses, and a pile of minified JavaScript. The question "was this application machine-generated" has to be answered from that alone, or not at all.

It is partly answerable. The part that is not is worth being clear about first.

What you cannot recover

The model is gone. A bundler has inlined, tree-shaken, minified and mangled the source. Variable names are single letters. Comments are stripped. Nothing in the output distinguishes code a language model produced from code a human produced, because the transformation that produced the bundle discards exactly the surface features anyone would key on.

Claims to the contrary generally rest on stylometry — brace placement, comment phrasing, identifier conventions — applied to source that no longer has any of those properties. Treat any product that offers to tell you which LLM wrote a minified bundle as making a claim it cannot support.

Hand-written code is indistinguishable from assisted code. Even with unminified source, a human who accepted an assistant's suggestion and a human who typed the same thing produce identical bytes. The distinction is a fact about the authoring session, not about the artefact.

What you can recover: the builder

AI app builders are a different matter. Tools that generate and host an entire application — v0, Lovable, bolt.new and their peers — leave deliberate marks, because they want attribution:

  • Generator meta tags. <meta name="generator" content="..."> is the oldest convention on the web and these tools still use it.
  • Tagger scripts. Several builders inject a first-party script whose filename or global object names the product, used for their own analytics and edit-in-place features.
  • Hosting fingerprints. Default subdomains on the builder's own domain, and response headers their platform adds.
  • Scaffold artefacts. A distinctive default favicon, a boilerplate route, a specific error page.

None of these is a statement about a model. They are statements about a build pipeline, which is a genuinely useful thing for an attack-surface inventory to know: it tells you the deployment is templated, that its dependency set is whatever the builder pinned, and that patching is likely to be the platform's decision rather than the owner's.

Confidence is part of the finding, not a footnote

Every marker should carry a confidence level, and low-confidence markers should be dropped before anything becomes an asset.

That sounds like ordinary hygiene. It is actually a direction-of-error decision, and it is the most consequential one in this whole feature.

Calling a customer's hand-written application machine-generated is worse than missing one that was. The false positive lands in an inventory, gets exported into a report, and eventually someone has to explain to an engineering team why their product is tagged as vibe-coded. The false negative costs a row in a table nobody was relying on.

So the bias runs hard against false positives:

  • a generator meta tag naming a known builder is high confidence — it is a self-declaration
  • a tagger script matching a known filename pattern is medium
  • a default favicon hash or a boilerplate route is low, and low never becomes an asset on its own

The list is a judgement call, and ours got smaller

When we reviewed the builder list, two entries came off it: Framer and plain Wix.

Both leave clear generator marks. Both are website builders. Neither is an AI code generator in the sense the finding implies — a Framer site is a designer's output, and labelling it "AI-generated" in an asset inventory is a claim the evidence does not make. The marker is real; the conclusion drawn from it was not.

This is the recurring failure mode in fingerprinting work. Detection accuracy and label accuracy are separate problems, and a detector can be perfectly accurate about the presence of a marker while the label attached to that marker is wrong. Reviewing the labels is a different exercise from testing the detector, and it needs doing by someone willing to argue that a correct detection should be thrown away.

Where it belongs in the inventory

Treat a builder fingerprint as an attribute of an asset, not as a finding. It is not a vulnerability. It is context that changes how other findings should be read:

  • an outdated dependency on a builder-hosted app may not be the owner's to patch
  • a templated deployment probably shares its configuration with thousands of others, so a misconfiguration is likely systemic rather than local
  • the security contact for the platform may be more useful than the security contact for the site

Record one asset per vendor rather than one per marker, keeping the highest-confidence evidence, and store the evidence string itself — the actual tag or filename that matched. A finding that says "v0" is an assertion. A finding that says <meta name="generator" content="v0.dev"> at /index.html is something the owner can check in ten seconds and either accept or dispute.

That is the difference between a scanner people trust and a scanner people mute.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.