Is 教材下载 safe?

Clean risk

教材下载 sends the textbook page URL and PDF link to sunodownload.net instead of downloading directly from the government platform.

On matched smartedu.cn/zxx.edu.cn textbook pages, the extension injects a download button next to the page title. Clicking it opens sunodownload.net with the current page URL and the detected cbern.com.cn PDF asset URL in the query string, so the file is resolved and served through that third-party site rather than fetched directly.

10Risk
Who publishes it

Tksoft - 1 other listing from the same operator, none carrying a finding

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Tksoft

Same store account

1 other listing published from this account, 5k+ users between them, none of them carrying a finding.

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

webRequest listener observes all URLs via remotely-controlled regex

At startup the service worker GETs sunodownload.net/api/gettextbookpattern; its 'message' field feeds new RegExp(), replacing the pattern used by webRequest.onCompleted on <all_urls>. 545 captured events crossed google.com and amazon.com.

Severity
High unwanted
Type
Unexpected
CWE
CWE-506
Source
Dynamic sandbox
What actually happens
You did this

Your browser loads any web page on any site.

The extension did this

The extension tests the request URL against a regex pattern that was downloaded from a third-party server at startup.

The pattern is not the one bundled in the extension; it is whatever sunodownload.net returned at startup. Dynamic analysis observed this listener firing on google.com, amazon.com, facebook.com, and other unrelated domains.

Captured request
GEThttps://sunodownload.net/api/gettextbookpattern

JSON: { "message": "<regex string>" }. Observed during dynamic analysis: HTTP 403 (server-side block during analysis), confirming the fetch fires automatically on service worker startup.

The code that does this

The webRequest listener and remote-regex fetch, exact source from background.js

Readable version
// background.js — full file, not minified//// LISTENER: fires on EVERY completed HTTP/S request in the browserchrome.webRequest.onCompleted.addListener(    function (details) {        // logs once per request — observed 545 times across all domains in DA        if (checkUrl(details.url)) {            // match → relay URL to content script            sendMessageToContentScript({ cmd: 'test', value: details.url }, null);        }    },    { urls: ['<all_urls>'] }   // no site restriction here);// Default pattern (scoped to cbern.com.cn textbook PDFs) …let urlPattern = /^https:\/\/r.*?cbern\.com\.cn.*?\/edu_product\/esp\/assets\/.*?\.pdf$/i;// … but replaced immediately on startup with whatever the remote server returns:fetch('https://sunodownload.net/api/gettextbookpattern')    .then(r => r.text())    .then(data => {        let patternData = JSON.parse(data);     // { "message": "<regex>" }        urlPattern = new RegExp(patternData.message);  // no validation    })    .catch(err => console.error(err));function checkUrl(url) {    return urlPattern.test(url);  // tests every request URL}
Remote configuration source
    • sunodownload.net

    Provides the live URL-matching regex at startup. This domain is unrelated to the extension's advertised education platforms (zxx.edu.cn, smartedu.cn).

Remote server supplies unvalidated regex injected via new RegExp()

At startup the service worker fetches JSON from sunodownload.net/api/gettextbookpattern and feeds 'message' to new RegExp(), replacing the URL pattern unvalidated; webRequest.onCompleted then applies it across all browsing on <all_urls>.

Severity
High unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

The extension's service worker starts.

The extension did this

A remote server at sunodownload.net supplies a regex string that immediately becomes the live URL-matching pattern for the extension's <all_urls> webRequest listener.

The server-supplied string is passed to new RegExp() with no shape check or allowlist. The code applies it unconditionally, any error is caught and logged without notifying the user.

The code that does this

Remote-regex injection, lines 31-42 of background.js

Readable version
// Startup fetch — fires automatically when the service worker loadsfetch('https://sunodownload.net/api/gettextbookpattern')    .then(response => response.text())    .then(data => {        // Server returns: { "message": "<regex string>" }        let patternData = JSON.parse(data);        // No allowlist, no shape check, no signature verification:        urlPattern = new RegExp(patternData.message);        // ↑ urlPattern is the module-level variable used by the        //   webRequest.onCompleted listener on <all_urls>    })    .catch(error => console.error(error));  // error is logged, no retry or fallback// This function is called on every browser HTTP/S request:function checkUrl(url) {    return urlPattern.test(url);  // tests against whatever the server sent}
What the remote server controls
  • URL-matching pattern
    ^https:\/\/r.*?cbern\.com\.cn.*?\/edu_product\/esp\/assets\/.*?\.pdf$

    The regex that decides which request URLs trigger the extension's content-script relay. Currently set at startup by the server.

  • Listener scope
    <all_urls>

    All HTTP/S requests in the browser are tested against this pattern, not just education-site requests.

Remote configuration endpoint
    • sunodownload.net

    Supplies the live URL-matching regex on every extension startup. Domain is unrelated to the extension's advertised education platforms (zxx.edu.cn, smartedu.cn).

Reproduce it yourself

Fetches the current regex from the remote endpoint and prints it. Run this to see what pattern the server is currently serving.

Requires
  • curl
  • Python 3 (for json.tool formatting)
check-textbook-pattern.sh · sh
#!/usr/bin/env sh# Fetch the live regex pattern that the extension applies to all browser URLs.# Run this to see what sunodownload.net is currently serving.curl -s 'https://sunodownload.net/api/gettextbookpattern' | python3 -m json.tool# Expected shape: { "message": "<regex string>" }# The 'message' value is passed verbatim to new RegExp() in background.js:36.
How to run it
  1. 1Run: sh check-textbook-pattern.sh
  2. 2Note the 'message' value; this is the live pattern applied to all HTTP requests.
  3. 3Compare to the fallback: /^https:\/\/r.*?cbern\.com\.cn.*?\/edu_product\/esp\/assets\/.*?\.pdf$/i

Education page and resource URLs sent to sunodownload.net on download click.

When you click the download button on a supported education page, the extension opens sunodownload.net with the current page URL and the matched resource URL, sending browsing URL and page-title context to a third-party textbook service.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

The user clicks the extension's download button on a supported education detail page.

The extension did this

The extension opens sunodownload.net/textbook with the current page URL and matched resource URL in the query string.

Captured request
GEThttps://sunodownload.net/textbook
Fields in the request
  • Your browsing URL
    https://basic.smartedu.cn/tchMaterial/detail?contentType=assets_document (illustrative)

    Identifies the education detail page you were viewing when the download button was clicked.

  • Page title or textbook context
    Textbook detail page (illustrative)

    Provides context about the education material associated with the request.

Observation

Observed during dynamic analysis: the content script responded on the target education site, but the tested page did not render the element needed to inject the download button, so the click path was not reached in that run.

What it can do

Permissions this extension asks for, as declared in version 1.6. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • Act on the current tab, but only after you click the extension

    activeTab

  • Run its own code inside the pages you visit

    scripting

  • Watch every request your browser makes

    webRequest

  • See every page you navigate to, as you navigate to it

    webNavigation

  • See the address and title of every tab you have open

    tabs

Where it sends data

Destinations our analysis observed 教材下载 contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • sunodownload.net

    教材下载 sends data to sunodownload.net. No other extension we have analysed sends data here.

Updated 30 September 2026nglmepjfnbgpemgiocobcmhbahkhligk