DAST scanner · passive

Mixed Content

An HTTPS page that pulls a script, stylesheet, frame or image over plain http:// hands every hop on the network a chance to rewrite it. isitsecure reads the HTML and first-party JavaScript it already captured, matches four resource-loading tags against a literal http:// URL, and grades a swapped script higher than a swapped picture. It sends no requests of its own.

DASTMediumLowMixed contentSnapshot-based

What it reads and what it reports

The scanner works entirely from the snapshot that URL ingestion produced: the rendered HTML plus the concatenated first-party JavaScript. Four regexes look for a resource-loading tag whose src or href starts with http://. Each match becomes a finding, graded by what an attacker could do with the swapped bytes.

Snapshot https://app.example rendered HTML + first-party JS <script src="http://cdn…"> <link href="http://cdn…"> <iframe src="http://…"> <img src="http://img…"> <script src="https://…"> no request is ever sent; only captured text is read Gate: is the page itself HTTPS? an http:// page cannot have mixed content → 0 findings Four tag regexes script[src]activeMEDIUM link[href]activeMEDIUM iframe[src]activeMEDIUM img[src]passiveLOW value must start with the literal text http:// Findings Active mixed content (script[src])MEDIUM · http://cdn.example/app.js Active mixed content (link[href])MEDIUM · http://cdn.example/site.css Active mixed content (iframe[src])MEDIUM · http://widget.example/ Passive mixed content (img[src])LOW · http://img.example/logo.png deduped by tag + URL · max 10 per tag confidence 0.9 · source DAST_URL

1URL ingestion already rendered the page with Playwright and kept its HTML and every first-party JavaScript file. The scanner joins those into one string. If the scanned URL is not https:// it returns before running a single regex: an HTTP page has no secure context to break.

2The first regex finds a <script> whose src starts with http://. A script is active content: whoever controls those bytes controls the page. Active → MEDIUM.

3<link href="http://…"> and <iframe src="http://…"> are treated the same way. A stylesheet can restyle a login form and a frame can show one; both are active and both are MEDIUM.

4<img src="http://…"> is passive: a swapped picture cannot run code, but the request leaks the page the user is on and the image can be replaced. Passive → LOW.

5The same tag and URL pair is reported once, and each tag type stops after ten matches. Every finding carries the page URL, the resource URL and the tag, with confidence 0.9. The https:// script was never a match: the pattern needs the literal text http://.

Built from the code, not a markdown doc

This scanner has no page under docs/scanners/. Everything here is taken from isitsecure/engine/scanners/mixed_content_scanner.py and the README scanner-table row ("http:// resources loaded on an HTTPS page"). The regexes, severities, the HTTPS gate and the per-tag cap of 10 are read directly from that file.

Why a script and an image get different grades

The page itself is protected by TLS, but a sub-resource fetched over http:// travels in the clear and without any proof of who sent it. Anyone on the path can read it and rewrite the reply. What that buys the attacker depends on the tag.

Address bar shows a lock: the page document came over TLS. GET http://cdn.example/app.js leaves the page in the clear. The reply is rewritten on its way back. The browser cannot tell. script[src] → active mixed content → MEDIUM img[src] → passive mixed content → LOW Browser https://app.example TLS to app.example payload runs asapp.example origin picture swapped,page URL leaked plain HTTP: readable and writable by every hop Network attacker shared Wi-Fi, an ISP, a proxy: any hop on the path Resource host http://cdn.example no TLS, no identity /app.js /logo.png

1The user opens https://app.example. The document arrives over TLS, so the browser shows a lock and the app's cookies and tokens live in that origin.

2The page contains <script src="http://cdn.example/app.js">. The browser fetches it over plain HTTP: no encryption, no certificate, nothing that says which server answered.

3A network attacker sitting on the path reads the reply and replaces it with app.js plus a payload. The bytes arrive looking exactly like a normal response.

4The altered script executes inside the HTTPS origin, with the page's DOM, storage and session. That is why script, stylesheet and frame tags are graded MEDIUM: the attacker gets code execution in the page.

5For <img src="http://img.example/logo.png"> the same hop can only swap the picture and see which page requested it. No code runs, so the tag is graded LOW.

Why it matters

Every tag the scanner flags is a place where the page's own TLS guarantee is quietly undone. The finding text says it plainly: active mixed content "can be intercepted and modified by a network attacker to execute arbitrary code in the page context", and passive mixed content "leaks the request and can be swapped". The fix in both cases is the same word: HTTPS.

What stays quiet

Three guards keep the finding list honest. The page must be HTTPS, the URL in the tag must literally begin with http://, and the same tag-and-URL pair is counted once. The rows below are four pages carrying the same kind of tag.

page · tag verdict http://app.example<script src="http://cdn.example/a.js"> quiet · the page is HTTPscanner returns before any regex runs https://app.example<script src="https://cdn.example/a.js"> quiet · scheme matches the pagethe regex needs the literal text http:// https://app.example<script src="//cdn.example/a.js"> quiet · protocol-relative inherits httpsno http:// prefix, so no match https://app.example<script src="http://cdn.example/a.js"> flagged · active mixed content · MEDIUMHTTPS page + http:// script, reported once per tag + URL

1Four pages, each loading the same script tag in a slightly different way. Only one of them is a problem, and the scanner has to tell them apart from text alone.

2The page at http://app.example is itself plain HTTP. There is no secure context to downgrade, so the scanner returns an empty list before running any regex.

3An https:// resource on an HTTPS page is exactly what should happen. The pattern only matches when the captured value starts with http://, so this line never fires.

4A protocol-relative //cdn.example/a.js inherits the page's scheme and loads over HTTPS. It has no http:// prefix, so it is quiet too.

5An HTTPS page loading http://cdn.example/a.js is the real case. It is reported as Active mixed content (script[src]), MEDIUM, once, even if the tag appears several times in the HTML and in the bundles.

Vulnerable vs fixed markup

The finding's own advice is one line: load it over HTTPS. The GOOD tab shows the same page with the resource moved to a TLS origin.

<!-- served from https://app.example -->
<head>
  <script src="http://cdn.example/app.js"></script>       <!-- active: MEDIUM -->
  <link rel="stylesheet" href="http://cdn.example/site.css"> <!-- active: MEDIUM -->
</head>
<body>
  <iframe src="http://widget.example/"></iframe>           <!-- active: MEDIUM -->
  <img src="http://img.example/logo.png" alt="">            <!-- passive: LOW -->
</body>

Reference: what the code does

ItemValueWhere it comes from
Inputsnapshot HTML + first-party JS, joined with a newlinehtml_content, first_party_js_content
Preconditionscanned URL scheme is https; otherwise no findingsurlparse(snapshot.url).scheme
Active tagsscript[src], link[href], iframe[src] → MEDIUM_ACTIVE_PATTERNS
Passive tagsimg[src] → LOW_PASSIVE_PATTERNS
Match ruleattribute value starts with the literal http://, case-insensitive tag namesthe four regexes
Dedup / capone finding per tag + URL; at most 10 per tag typeseen, _MAX_FINDINGS_PER_KIND
Findingcategory MIXED_CONTENT, source DAST_URL, confidence 0.9, method GET, endpoint = page URL_build_finding
Networknone: the scanner never fetches the resourcemodule docstring
Scan depthruns at both quick and deepfactory.py DAST list
Things it does not do

It does not fetch the resource, so it cannot tell whether the http:// host would have redirected to HTTPS. It does not read CSS url() values, <video>, <audio>, <object> or fetch() calls, and it does not evaluate a Content-Security-Policy: upgrade-insecure-requests header that would have fixed the problem at the browser.