Is Grammar Checker safe?

High risk

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.…

LinangDatav1.0.10Chrome Web Store
77Risk
Who publishes it

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.

Publisher
LinangData

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
What actually happens
You did this

You open the Grammar Checker popup.

The extension did this

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.

Captured request
GEThttps://linangdata.com/servedcontent/dynamiclinks.php?source=grammarChecker1776484122697

HTTP 200 with 1370 bytes of HTML; observed content included a navigation link for AI Chess Tutor.

Remote content placed into the popup
  • Remote content host
    linangdata.com

    Lets a remote server provide part of the extension popup you see.

  • Requested page
    https://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 code that does this

The popup request and insertion path

Readable version

Readable callback from the deobfuscated app.js

app.js
try {  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>
Remote host involved
    • 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
What actually happens
You did this

You open the Grammar Checker popup.

The extension did this

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.

Captured request
GEThttps://linangdata.com/servedcontent/bannertop.php?source=grammarChecker1776484122697

HTTP 200 with an empty response body in the observed session.

Captured request
GEThttps://linangdata.com/servedcontent/bannerbottom.php?source=grammarChecker1776484122697

HTTP 200 with an empty response body in the observed session.

Banner requests and popup targets
  • Top banner request
    https://linangdata.com/servedcontent/bannertop.php?source=grammarChecker1776484122697

    Lets a remote server provide the content for the top of the popup.

  • Bottom banner request
    https://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 response
    HTTP 200 with an empty body

    Shows what the banner endpoints returned during the recorded test session.

The code that does this

The banner callbacks insert non-empty responses

Readable version

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>
Remote host involved
    • linangdata.com

    Serves the bannertop.php and bannerbottom.php responses that the extension can insert into the popup banners.

Updated 30 September 2026mpeepmfabickbdbckcejbflkpfamgcon