RAG Ingest Injection Scanner

Drop a document you are about to index. The page computes two texts: what a human sees when they open it, and what a text extractor with no CSS engine hands the model. The output is the delta.

1. A zero delta is not a safe document

This tool finds text the reviewer never sees. Injections in plain visible body text are just as effective and will not appear here at all. An empty delta means no hidden layer was found, nothing more. Read the visible text too.

2. Subresource blocking is a caveat on the measurement itself

The preview declares default-src 'none', so images, remote fonts, remote stylesheets and scripts never load. That is deliberate: rendering an untrusted document must not phone home. It also means layout that depends on a background image, a web font metric or an external stylesheet is not the layout your reader gets, so a verdict on such a node can be wrong. Every check that could be inverted by a blocked subresource refuses to return a verdict instead of guessing.

Input

Extractor profile

The default is the stripped profile on purpose. Mimicking an extractor with raw text alone dumps entire style and script bodies into the delta and pads the headline number with content no mainstream loader ever sends the model. Switch profiles on the sample to watch that happen.

What counts as hidden, and what this tool refuses to decide

A finding is only reported when the decision is mechanical: a computed style value, a geometry measurement, or a tag name. Anything that needs a human eye is filed under suspect and is never counted in the delta.

Text colour against background colour is the classic example, and it is deliberately demoted. getComputedStyle hands back a gradient as a function string, not a colour: running the sample on 2026-08-11 in Edge 151.0.4129.78 headless, the gradient paragraph reported background-image: linear-gradient(90deg, rgb(17, 34, 51), rgb(68, 85, 102)), which no contrast formula can consume. And because this tool blocks subresources, a node whose real background is an image is read here against whatever colour sits behind the blocked image, which can invert the verdict outright. So any node whose ancestor chain contains a background image, a filter, a backdrop-filter, a blend mode, or an opacity below 1 gets no verdict, only a stated reason.

Rendered preview (the measurement surface)

This frame is visible on purpose and is never hidden or detached. MDN, HTMLElement.innerText: If the element itself is not being rendered (for example, is detached from the document or is hidden from view), the returned value is the same as the Node.textContent property. Measure inside a hidden frame and the visible text collapses onto the extractor text, the delta goes to zero, and every document is reported clean. The frame is sandboxed with allow-same-origin and without allow-scripts, which MDN documents as blocking script execution in the frame while still letting a same-origin parent read its DOM.

Measured while building this page, 2026-08-11, Edge 151.0.4129.78 headless

Two runs against the built-in sample, both of which shaped the code above. First: with the sample in a rendered frame, innerText omitted the display:none payload while textContent kept it; loading the identical markup into a display:none frame made innerText character-for-character equal to textContent, which is the failure mode the visible frame exists to avoid. Second, and the reason this page does not simply diff innerText against textContent: in the rendered frame innerText still returned the clip-path payload and the opacity:0 payload. Rendering tells you about display and visibility. Everything else has to be decided per text node, which is what the rules below do.

Results

Nothing analyzed yet.

Press Load sample HTML then Analyze, or paste your own. The sample is a vendor policy that reads as ordinary prose and puts ten segments in the gap on the default profile. Switch to the raw profile and three more appear, which turn out to be CSS, JavaScript and template markup rather than hidden prose: that gap between thirteen and ten is the reason the default is the stripped profile.

One of the ten, the noscript block, does render in the preview. Scripting is disabled in there, which is exactly the inversion the rule table explains.

How each rule decides

Every rule below is decided from a computed style value, a geometry measurement or a tag name. No rule needs a judgement call.
RuleDecided byWhy the extractor still sees it

Extractor behaviour, read from source

External material

The table below is not this tool's computation. It is what four HTML readers and two DOCX readers do with the channels above, read from their source on the date stamped here. Library behaviour changes; the logic on this page does not depend on any row being current. Each row is an inference from reading the linked code, not a run I performed.

ReaderChannelReaches the modelSource read