Is Grammar Checker safe?
Grammar is high risk. Opening the Grammar Checker popup requests HTML from linangdata.com and inserts it with jQuery .html(). Analysis captured a GET to dynamiclinks.php returning 1370 bytes of HTML with a Linang Data link; popup code assigns it to #links.…
Who publishes itLinangData - 6 other listings from the same operator, 1 of them carrying a finding
LinangData - 6 other listings from the same operator, 1 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
6 other listings published from this account, 151k+ users between them. 1 of them carries a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Remote HTML loads into the popup menu
Opening the Grammar Checker popup requests HTML from linangdata.com and inserts it with jQuery .html().
Analysis captured a GET to dynamiclinks.php returning 1370 bytes of HTML with a Linang Data link; popup code assigns it to #links.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You open the Grammar Checker popup.
The extension downloads HTML from linangdata.com and places it into the popup menu.
The shipped code sends the request on DOMContentLoaded and assigns the response to the #links element.
HTTP 200 with 1370 bytes of HTML; observed content included a navigation link for AI Chess Tutor.
- Remote content hostlinangdata.com
Lets a remote server provide part of the extension popup you see.
- Requested pagehttps://linangdata.com/servedcontent/dynamiclinks.php?source=grammarChecker1776484122697
Identifies the remote page that supplies the popup menu markup.
- Returned menu item<li class="nav-item"><a class="nav-link nav-link-dropdown navopentab py-0" href="https://linangdata.com/ai-chess-tutor/">AI Chess Tutor</a></li>
Shows that server-provided HTML becomes part of the extension interface.
- Popup target#links
Shows where the returned HTML appears inside the popup.
The popup request and insertion path
Readable callback from the deobfuscated app.js
app.jstry { const uuid = new Date().getTime(); $.get( `https://linangdata.com/servedcontent/dynamiclinks.php?source=grammarChecker${uuid}`, function( data ) { $( "#links" ).html( data ); $(".navopentab").unbind().on("click", function(e){ e.preventDefault(); var link = $(this).attr('href'); chrome.tabs.create({url:link}); }) });} catch (err) {}Readable popup target from index.html
index.html<ul id="links" class="dropdown-menu" aria-labelledby="navbarScrollingDropdown"></ul>- linangdata.com
Serves the dynamiclinks.php HTML that the extension inserts into the popup menu.
Banner endpoints feed the popup DOM
Opening the popup requests banner content from linangdata.com, inserting any non-empty response via jQuery .html().
GETs to bannertop.php/bannerbottom.php returned 200 with empty bodies; both use the same insertion.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You open the Grammar Checker popup.
The extension requests banner HTML from linangdata.com and can insert non-empty responses into the popup.
The observed banner responses were empty, but the code path for top and bottom banner insertion is present and runs on popup load.
HTTP 200 with an empty response body in the observed session.
HTTP 200 with an empty response body in the observed session.
- Top banner requesthttps://linangdata.com/servedcontent/bannertop.php?source=grammarChecker1776484122697
Lets a remote server provide the content for the top of the popup.
- Bottom banner requesthttps://linangdata.com/servedcontent/bannerbottom.php?source=grammarChecker1776484122697
Lets a remote server provide the content for the bottom of the popup.
- Top banner target#banner-top
Shows where non-empty top banner HTML would appear in the extension interface.
- Bottom banner target#banner-bottom
Shows where non-empty bottom banner HTML would appear in the extension interface.
- Observed banner responseHTTP 200 with an empty body
Shows what the banner endpoints returned during the recorded test session.
The banner callbacks insert non-empty responses
Readable top banner callback
app.js$.get( `https://linangdata.com/servedcontent/bannertop.php?source=grammarChecker${uuid}`, function( data ) { if (data) { $( "#banner-top" ).html( data ); } $(".navopentab").unbind().on("click", function(e){ e.preventDefault(); var link = $(this).attr('href'); chrome.tabs.create({url:link}); })});Readable bottom banner callback
app.js$.get( `https://linangdata.com/servedcontent/bannerbottom.php?source=grammarChecker${uuid}`, function( data ) { if (data) { $( "#banner-bottom" ).html( data ); } $(".navopentab").unbind().on("click", function(e){ e.preventDefault(); var link = $(this).attr('href'); chrome.tabs.create({url:link}); })});Readable banner containers from index.html
index.html<div id="banner-top"></div><div id="banner-bottom"></div>- linangdata.com
Serves the bannertop.php and bannerbottom.php responses that the extension can insert into the popup banners.