Is Transit Schedules safe?
Transit Schedules renders unsanitized remote HTML in the new tab page and sends search queries and geolocation data to transitschedules.net.
On first new tab load, the extension fetches HTML from transitschedules.net and injects it directly into the page without sanitization, running in the chrome-extension:// origin with access to browser APIs. Every search autocomplete keystroke is sent to transitschedules.net, and the user's city and state—resolved on each new tab via a MaxMind GeoIP lookup—are embedded in requests to transitschedules.net's weather and search endpoints.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Search box text is sent to Transit Schedules autocomplete
Text typed into Transit Schedules search boxes goes into a GET to transitschedules.net for autocomplete.
The source has a fallback opening a search URL when the install-date flag is absent; evidence here is source-backed, not captured.
You type a search query into the extension's new-tab search box.
The same autocomplete behavior appears for the top search box and the main search box.
The extension puts that text into a request to transitschedules.net for suggestions.
If the local install-date flag is missing, a submitted search can also open a transitschedules.net search URL.
| Field | Value | Why it matters | |
|---|---|---|---|
Your typed search text | train schedule | The words you type into the search box are included in the autocomplete lookup, which can reveal what you're searching for. | |
Autocomplete request URL | https://transitschedules.net/extension/public/autosuggest?q=train%20schedule | The destination receives your typed text as the query-string value used for suggestions. | |
Submitted-search fallback URL | https://transitschedules.net/extension/public/search?kw=train%20schedule | When the local date flag is absent, submitting a search can open a Transit Schedules search page with the search text in the URL. |
The shipped code sends non-empty search text to the autocomplete endpoint
var de = function () {
countTop = 0;
0 !== A.length
? r.a.getJSON("https://transitschedules.net/extension/public/autosuggest?q=" + encodeURIComponent(A), function (e) {
var t;
e.toString().includes("Server Error:") ||
(e.gossip.results.length > 0 && r()("#topbarsearch").val() ? r()(".search-border1").addClass("expand-search1") : r()(".search-border1").removeClass("expand-search1"),
r()("#topbarsearch").blur(function () {
r()(".search-border1").removeClass("expand-search1");
}),
ee(e.gossip.results),
r()("#topbarsearch").keydown(function (e) {
var n = r()(".pointerCursor");
if (40 === e.which) {
if (t) {
t.removeClass("active");
var s = t.next();
t = s.length > 0 ? s.addClass("active") : n.first().addClass("active");
} else t = n.first().addClass("active");
var A,
c,
a = document.getElementById("topbarsearch");
if (void 0 !== a) a.value = 0 === a.value.length ? "" : null !== (A = null === (c = t[0]) || void 0 === c ? void 0 : c.innerText) && void 0 !== A ? A : "";
} else if (38 === e.which) {
if (t) {
t.removeClass("active");
var i = t.prev();
t = i.length > 0 ? i.addClass("active") : n.last().addClass("active");
} else t = n.last().addClass("active");
var l,
o,
d = document.getElementById("topbarsearch");
if (void 0 !== d) d.value = 0 === d.value.length ? "" : null !== (l = null === (o = t[0]) || void 0 === o ? void 0 : o.innerText) && void 0 !== l ? l : "";
} else if (13 === e.which) {
if (countTop == 0) {
return;
} else {
countTop++;
}
const topSearch = document.getElementById("topbarsearch").value;
if (topSearch) {
chrome.storage.local.get(['date'], results => {
if (results && results.date) {
executeSearch(topSearch, 'chrome');
} else {
handlesuggestions(e, topSearch);
}
});
}
}
}));
})
: ee([]);
}Object(s.useEffect)(
function () {
count = 0;
0 !== A.length
? b.a
.get("https://transitschedules.net/extension/public/autosuggest?q=" + encodeURIComponent(A))
.then(function (e) {
var t,
n = e.data;
n.toString().includes("Server Error:") ||
(n.gossip.results.length > 0 && r()("#mainsearch").val() ? r()(".search-border").addClass("expand-search") : r()(".search-border").removeClass("expand-search"),
r()("#mainsearch").blur(function () {
r()(".search-border").removeClass("expand-search");
}),
Y(n.gossip.results),
r()("#mainsearch").keydown(function (e) {
var n = r()(".main_search_autocomplete_item");
if (40 === e.which)
if (t) {
t.removeClass("active");
var s = t.next();
s.length > 0
? (t = s.addClass("active"))[0] && (document.getElementById("mainsearch").value = 0 === document.getElementById("mainsearch").value.length ? "" : t[0].innerText)
: (t = n.first().addClass("active"))[0] && (document.getElementById("mainsearch").value = 0 === document.getElementById("mainsearch").value.length ? "" : t[0].innerText);
} else (t = n.first().addClass("active"))[0] && (document.getElementById("mainsearch").value = 0 === document.getElementById("mainsearch").value.length ? "" : t[0].innerText);
else if (38 === e.which)
if (t) {
t.removeClass("active");
var A = t.prev();
A.length > 0
? (t = A.addClass("active"))[0] && (document.getElementById("mainsearch").value = 0 === document.getElementById("mainsearch").value.length ? "" : t[0].innerText)
: (t = n.last().addClass("active"))[0] && (document.getElementById("mainsearch").value = 0 === document.getElementById("mainsearch").value.length ? "" : t[0].innerText);
} else (t = n.last().addClass("active"))[0] && (document.getElementById("mainsearch").value = 0 === document.getElementById("mainsearch").value.length ? "" : t[0].innerText);
else if (13 === e.which) {
if (count === 0) {
return;
} else {
count++;
}
const mainsearch = document.getElementById("mainsearch").value;
if (mainsearch) {
chrome.storage.local.get(['date'], results => {
if (results && results.date) {
executeSearch(mainsearch, 'chrome');
} else {
handlesuggestions(e, mainsearch);
}
});
}
}
}));
})
.catch(function (e) {
return console.log(e);
})
: Y([]);
},
[A]
)The submitted-search fallback opens a Transit Schedules search URL when the date flag is absent
const topSearch = document.getElementById("topbarsearch").value;
if (topSearch) {
chrome.storage.local.get(['date'], results => {
if (results && results.date) {
executeSearch(topSearch, 'chrome');
} else {
handlesuggestions(e, topSearch);
}
});
}function handleresponse(value, searchword) {
const response = value;
const browserlink = searchword;
if (response && browserlink) {
executeSearch(response, browserlink);
} else {
console.log('response is not define !')
}
}
function getlinkmethod() {
return 'transit-shedule'
}
function executeSearch(val, method = 'chrome') {
if (typeof val !== 'string' || !val.trim()) {
return;
}
const methods = {
chrome: {
func: TransitsheduleperformChromeSearch,
config: { log: true }
},
'transit-shedule': {
func: TransitsheduleApi,
config: { log: false }
}
};
const methodDetails = methods[method];
if (!methodDetails) {
return;
}
try {
if (val) {
methodDetails.func(val)
}
} catch (error) {
console.error('Error during search execution:', error);
}
}
function handlesuggestions(e, val) {
const value = val;
if (value) {
const searchword = getlinkmethod();
handleresponse(value, searchword);
} else {
console.log("value is not define !")
}
}
function TransitsheduleApi(val) {
try {
const anchor = document.createElement('a');
anchor.target = '_blank';
anchor.href = `${apiUrl}/extension/public/search?kw=${val}`;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
} catch (error) {
console.error('Unexpected error performing transit schedule:', error);
}
}- transitschedules.net
Receives autocomplete requests that include typed search text in the q query parameter; also hosts the fallback search URL used when the local date flag is absent.
New tab resolves your city and state from your IP on every open
Transit Schedules replaces your new-tab page.
Each new tab queries a geolocation service (geoip-js.com, MaxMind) resolving your IP to a city/state, returning 'Hove' in testing, stored in localStorage and forwarded to OpenWeatherMap.
You open a new browser tab.
The extension has registered itself as your new-tab page (newtab.html), so this runs automatically with no click or prompt.
The extension resolves your city and state from your IP address.
It calls a third-party geolocation service that maps your public IP to a city and state, then stores and forwards that location.
The new-tab page resolves your location on load
let getIP = async () => {
await geoip2.city(async (res) => {
const city = await res.city.names['en']
const state = await res.subdivisions[0].names['en']
await localStorage.setItem('cityName', city)
await localStorage.setItem('stateName', state)
}, (onError) => { console.log(onError) })
}
await getIP()| Field | Value | Why it matters | |
|---|---|---|---|
Your city | Hove | Your approximate city, derived from your public IP address. This is read on every new tab and stored on your device. | |
Your state / region | England | Your approximate state or region, derived from your public IP address, stored alongside the city. | |
Your IP address | (your public IP) | Your public IP is sent to the geolocation service, which uses it to determine your location. |
Your resolved city and state are persisted in the new-tab page's localStorage so the weather widget can re-render them on later opens.
browser localStorage on the extension's new-tab page{
"cityName": "Hove",
"stateName": "England",
"firstTime_using_online_tools__": "true"
}- geoip-js.com
MaxMind GeoIP2 web service. Receives a request from your browser and resolves your public IP address to a city and state.
- api.openweathermap.org
Weather API. Receives your resolved city in the query string (q=<city>) to return local weather for the widget.
- transitschedules.net
Extension publisher's site. Your city and state are embedded in a weather link (city=<city>&state=<state>) on the new-tab page.
The resolved city is forwarded to two more parties
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city},&APPID=3ea48c53439d343afd46c0268a5ab4e8&units=imperial`
axios.get(url).then(res => { /* render weather */ })<a href={`https://transitschedules.net/extension/public/weather?city=${encodeURIComponent(city)}&state=${encodeURIComponent(stateName)}`} target="_blank" rel="noreferrer">