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.
What actually happens
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.
Data placed into Transit Schedules request URLs
- Your typed search texttrain 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 URLhttps://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 URLhttps://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.
Captured request
No live autocomplete response was captured in the prior dynamic-analysis attempt; the shipped source constructs this GET request when search text is non-empty.
The code that does this
The shipped code sends non-empty search text to the autocomplete endpoint
Top search box autocomplete handler
static/js/main.79a63e01.chunk.jsvar 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([]);}Main search box autocomplete effect
static/js/main.79a63e01.chunk.jsObject(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 code that does this
The submitted-search fallback opens a Transit Schedules search URL when the date flag is absent
Missing-date branch in the top search submit path
static/js/main.79a63e01.chunk.jsconst topSearch = document.getElementById("topbarsearch").value;if (topSearch) { chrome.storage.local.get(['date'], results => { if (results && results.date) { executeSearch(topSearch, 'chrome'); } else { handlesuggestions(e, topSearch); } });}Search dispatch and Transit Schedules URL opener
static/js/main.79a63e01.chunk.jsfunction 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); }}External service receiving the search request
- 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.
What actually happens
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 code that does this
The new-tab page resolves your location on load
ip.js — runs on every new tab load
js/ip.jslet 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()Captured request
200 OK. JSON body resolving the caller's IP to a city/state. Observed during dynamic analysis returning city.names.en = 'Hove' with subdivisions data; values then written to localStorage.
What the geolocation response yields and where it goes
- Your cityHove
Your approximate city, derived from your public IP address. This is read on every new tab and stored on your device.
- Your state / regionEngland
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.
What's stored on your device
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.
- Location
- browser localStorage on the extension's new-tab page
{ "cityName": "Hove", "stateName": "England", "firstTime_using_online_tools__": "true"}Hosts that receive your IP or resolved location
- 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 code that does this
The resolved city is forwarded to two more parties
City passed to OpenWeatherMap
static/js/main.79a63e01.chunk.jsconst url = `https://api.openweathermap.org/data/2.5/weather?q=${city},&APPID=3ea48c53439d343afd46c0268a5ab4e8&units=imperial`axios.get(url).then(res => { /* render weather */ })City + state embedded in publisher link
static/js/main.79a63e01.chunk.js<a href={`https://transitschedules.net/extension/public/weather?city=${encodeURIComponent(city)}&state=${encodeURIComponent(stateName)}`} target="_blank" rel="noreferrer">What it can do
Permissions this extension asks for, as declared in version 1.0.5. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Run searches with your default search engine
search
Store data in your browser
storage
Where it sends data
Destinations our analysis observed Transit Schedules 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.
- api.openweathermap.orgwidely used
Transit Schedules sends data to api.openweathermap.org. A widely used service: 33 other extensions we have analyzed send data here.
- geoip-js.com
Transit Schedules sends data to geoip-js.com. 5 other extensions we have analyzed send data here.
- transitschedules.net
Transit Schedules sends data to transitschedules.net. 2 other extensions we have analyzed send data here.
