Is View Manuals safe?
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.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
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.
You type into the View Manuals search box on the new tab page.
The extension sends the current search text to View Manuals for autosuggest results.
| Field | Value | Why it matters | |
|---|---|---|---|
Search box text | printer manual | The 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%20manual | Putting 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-top | Both visible search fields use handlers that can send the text you type. |
| When | You did | Extension 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. |
Both search input handlers send the current field value
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);
}
});
});
});$("#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);
}
});
});
});The extension routes the browser new tab to the page with these handlers
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);
}<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>
- viewmanuals.com
Receives autosuggest GET requests whose searchTerm query parameter is the current text from the new-tab search field.
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.
You open the View Manuals new tab page for the first time in a browser profile.
The extension requests remote pixel content and writes the response into the page as HTML.
| Field | Value | Why it matters | |
|---|---|---|---|
Pixel endpoint | https://viewmanuals.com/admin/public/pixels | This is the remote service the new tab page contacts before adding markup to the page. | |
Response field | res.data.data | This 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=true | This browser-side flag controls whether the pixel request runs again after a successful load. | |
DOM target | #setPixelImage | This is the page location where the server response is inserted as HTML. |
The new tab page requests remote HTML and inserts it into the DOM
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);
});
}The install handler also contacts the pixel endpoint
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) { });
}
});- viewmanuals.com
Receives the pixel request and returns the HTML data that the new tab page inserts into #setPixelImage.