Is View Manuals safe?

Medium risk

View Manuals sends every keystroke typed in its search bar to viewmanuals.com in real time.

Each character entered into the new tab search box triggers an immediate request to the operator's autosuggest endpoint, transmitting the partial query without debouncing or opt-out. Additionally, the new tab page fetches server-supplied HTML from viewmanuals.com on first load and inserts it into the DOM without sanitization, allowing operator-controlled content including third-party tracking pixels.

View Manualsv1.0.0.1Chrome Web Store
45Risk

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

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Search keystrokes sent to View Manuals

When you type in either View Manuals search box, the extension sends the current text to viewmanuals.com as the `searchTerm` parameter.

This is tied to the input handlers, so it fires as text changes, not only after you submit a search.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You type into the View Manuals search box on the new tab page.

The extension did this

The extension sends the current search text to View Manuals for autosuggest results.

02EvidenceFIELD TABLE
Fields carried by the autosuggest request
FieldValueWhy it matters
Search box text
printer manualThe words you type into the new tab search field are sent as the autosuggest term.
Request URL
https://viewmanuals.com/admin/public/autosuggest?searchTerm=printer%20manualPutting the term in the URL can expose the text to browser, proxy, and server logs along the request path.
Input selectors
#search-box and #search-topBoth visible search fields use handlers that can send the text you type.
03EvidenceCORRESPONDENCE
How typing maps to outbound autosuggest requests
WhenYou didExtension did
on input
user
You type a non-empty value into the main search box.
extension
The extension sends the current value to the autosuggest endpoint.
on input
user
You type a non-empty value into the top search box.
extension
The extension sends that current value to the same autosuggest endpoint.
04EvidenceNETWORK CAPTURE
Captured request
GEThttps://viewmanuals.com/admin/public/autosuggest?searchTerm=printer%20manual
05EvidenceCODE COMPARE
The code that does this

Both search input handlers send the current field value

What it actually does
Main search box input handlerscript.js
let searchTerm;
$("#search-box").on("input", async function (e) {
  searchTerm = e.target.value;

  let removeSpace = searchTerm.trim();
  if (removeSpace.length > 0) {
    const rawResponse = await axios
      .get(
        "https://viewmanuals.com/admin/public/autosuggest?searchTerm=" +
        searchTerm,
      );
    const content = await rawResponse.data;
    const data = content.gossip.results;
    let word;
    // Display search suggestions
    const suggestionList = data
      .map((suggestion) => {
        var listitem = suggestion.key;
        word = "<span>" + listitem.substr(0, removeSpace.length) + "</span>";
        word += "<b>" + listitem.substr(removeSpace.length) + "</b>";
        if (word == searchTerm) {
          return "";
        } else {
          return `<li class="autocomplete"><a data-id='${listitem}'><i class="fa-solid fa-magnifying-glass"></i>${word}</a></li>`;
        }
      })
      .join("");
    $('#suggestions').html(suggestionList);
  }
  const element = searchBox.value.length;
  if (removeSpace.length && element > 0) {
    $(".search_results").addClass("open");
    $(".modal_search_results").addClass("open");
    $('body').addClass('body_height');
    $("#suggestions").removeClass('clo')
    $("#suggestions").removeClass('closed')
    $(".for_open").addClass("open");
    $(".search_icon").addClass("close");
    $(".icon_show").removeClass("close");
    $("#search-icon").html(
      `<a class="search__suggestions" target="_blank" data-id='${searchTerm}'><i class="fa-solid fa-magnifying-glass"></i></a>`
    );
    $("input").parent().addClass("focus");
    $(".search_bar").removeClass("empty");
    if ($(".search_results").is(":empty")) {
      $(".search_bar").addClass("empty");
    }
  } else {
    // input value null to show fist like input
    $("#suggestions").addClass('closed')
    $(".search_results").removeClass("open");
    $(".modal_search_results").removeClass("open");
    $(".for_open").removeClass("open");
    $(".search_icon").removeClass("close");
    $(".icon_show").addClass("close");
    $("input").parent().removeClass("focus");
    $('body').removeClass('body_height');
  }

  $(".autocomplete").click(function (e) {
    e.preventDefault();
    const url = $(this).find('a').attr('data-id')
    chrome.storage.local.get(['date'], results => {
      if (results && results.date) {
        executeSearch(url, 'chrome');
      } else {
        handlesuggestions(e, url);
      }
    });
  });
  $(".search__suggestions").click(function (e) {
    e.preventDefault();
    const val = $(this).attr('data-id');
    chrome.storage.local.get(['date'], results => {
      if (results && results.date) {
        executeSearch(val, 'chrome');
      } else {
        handlesuggestions(e, val);
      }
    });
  });
});
Top search box input handlerscript.js
$("#search-top").on("input", async function (e) {
  let searchTop;
  searchTop = e.target.value;
  let removeSpace = searchTop.trim();
  if (removeSpace.length > 0) {

    const rawResponse = await axios
      .get(
        "https://viewmanuals.com/admin/public/autosuggest?searchTerm=" +
        searchTop,
      );
    const content = await rawResponse.data;

    const data = content.gossip.results;
    let word;
    // Display search suggestions
    const suggestionList = data
      .map((suggestion) => {
        var listitem = suggestion.key;
        word = "<span>" + listitem.substr(0, removeSpace.length) + "</span>";
        word += "<b>" + listitem.substr(removeSpace.length) + "</b>";
        if (word == searchTop) {
          return "";
        } else {
          // add target to navigate new-tab link
          return `<li class="autocomplete-top"><a data-id='${listitem}'><i class="fa-solid fa-magnifying-glass"></i>${word}</a></li>`;
        }
      })
      .join("");
    $('#sub-suggestions').html(suggestionList);
  }
  const element = searchTopBox.value.length;
  if (removeSpace.length && element > 0) {
    $(".sub-search_results").addClass("open");
    $("#sub-suggestions").removeClass('closed')
    $(".sub-for_open").addClass("open");
    $(".sub-search_icon").addClass("close");
    $("#sub-search-icon").removeClass("close");
    $("#sub-search-icon").html(
      `<a class="sub-search__suggestions" target="_blank" data-id='${searchTop}'><i class="fa-solid fa-magnifying-glass"></i></a>`
    );
    $("#top-search-bar").addClass("top");
    // $(".search_bar").removeClass("empty");
    // if ($(".search_results").is(":empty")) {
    // $(".search_bar").addClass("empty");
    // }
  } else {
    // input value null to show fist like input
    $("#sub-suggestions").addClass('closed')
    $(".sub-search_results").removeClass("open");
    $(".sub-for_open").removeClass("open");
    $(".sub-search_icon").removeClass("close");
    $("#sub-search-icon").addClass("close");
    $("#top-search-bar").removeClass("top");
  }
  $(".autocomplete-top").click(function (e) {
    e.preventDefault();
    const url = $(this).find('a').attr('data-id');
    chrome.storage.local.get(['date'], results => {
      if (results && results.date) {
        executeSearch(url, 'chrome');
      } else {
        handlesuggestions(e, url);
      }
    });
  });
  $(".sub-search__suggestions").click(function (e) {
    e.preventDefault();
    const val = $(this).attr('data-id');
    chrome.storage.local.get(['date'], results => {
      if (results && results.date) {
        executeSearch(val, 'chrome');
      } else {
        handlesuggestions(e, val);
      }
    });
  });
});
06EvidenceCODE COMPARE
The code that does this

The extension routes the browser new tab to the page with these handlers

What it actually does
New-tab redirect bootstraprefresh.js
const version = parseInt(
    /Chrome\/([0-9.]+)/.exec(window.navigator.userAgent)[1]
);
if (version > 82) {
    window.location.href = `/index.html`;
} else {
    const meta = document.createElement("meta");
    meta.httpEquiv = "refresh";
    meta.content = "0;url=extension://index.html";
    document.querySelector("head").appendChild(meta);
}
Search inputs in the rendered new tab pageindex.html
<input id="search-top" type="text" placeholder="Search">
<input id="search-box" type="text" placeholder="Search or type a URL">
<script src="./script.js?version=2"></script>
07EvidenceTHIRD PARTY LIST
External host contacted by autosuggest
  • viewmanuals.com

    Receives autosuggest GET requests whose searchTerm query parameter is the current text from the new-tab search field.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Remote HTML inserted on the new tab page

When View Manuals' new tab loads for a profile without its local display flag set, the extension requests HTML from viewmanuals.com and inserts the response as markup.

The source shows it's inserted without sanitization.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open the View Manuals new tab page for the first time in a browser profile.

The extension did this

The extension requests remote pixel content and writes the response into the page as HTML.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://viewmanuals.com/admin/public/pixels
03EvidenceFIELD TABLE
Concrete values used by the pixel-loading path
FieldValueWhy it matters
Pixel endpoint
https://viewmanuals.com/admin/public/pixelsThis is the remote service the new tab page contacts before adding markup to the page.
Response field
res.data.dataThis field is treated as page markup, so whatever the server returns there can change what appears in the new tab page.
Local display flag
displayPixel=trueThis browser-side flag controls whether the pixel request runs again after a successful load.
DOM target
#setPixelImageThis is the page location where the server response is inserted as HTML.
04EvidenceCODE COMPARE
The code that does this

The new tab page requests remote HTML and inserts it into the DOM

What it actually does
if (!localStorage.getItem("displayPixel")) {
  axios
    .get(`${apiUrl}/admin/public/pixels`)
    .then((res) => {
      $("#setPixelImage").html(res.data.data);
      localStorage.setItem("displayPixel", true);
    })
    .catch((err) => {
      console.log(err);
    });
}
05EvidenceCODE COMPARE
The code that does this

The install handler also contacts the pixel endpoint

What it actually does
chrome.runtime.onInstalled.addListener(function (details) {
  if (details.reason === "install") {
    fetch(`${apiUrl}/admin/public/install`);
    fetch(`${apiUrl}/admin/public/pixels`);
    chrome.windows.getAll((wins) =>
      wins.forEach(
        (win) => win.type === "popup" && chrome.windows.remove(win.id)
      )
    );
    const currentDate = new Date();
    const getFormateDate = formatDate(currentDate);
    chrome.storage.local.set({ date: getFormateDate });
    chrome.tabs.create({}, function (tab) { });
  } else if (details.reason === "chrome_update") {
    chrome.tabs.create({}, function (tab) { });
  } else if (details.reason === "update") {
    chrome.tabs.create({}, function (tab) { });
  }
});
06EvidenceTHIRD PARTY LIST
External host contacted by this behavior
  • viewmanuals.com

    Receives the pixel request and returns the HTML data that the new tab page inserts into #setPixelImage.

Data recipients

viewmanuals.com
Updated 17 September 2026gnjggbjjfgjdcnognnfjgcgjbpjdephi