Linked Posts
Introduction
I would like to draw attention to a recent problem that I had while using TiddlyWiki in Firefox browser (93.0. 64-bit) in conjunction with the ~ExpressVPN extension / Add-on for Firefox.
The Problem
I was exporting JSON and .tid files from my standalone tiddlwiki so that I could get help on a problem I was having with filtering tagged tiddlers.
Importing the files into a TW gave the following error messages
imported JSON file has error
{
"tiddlers": {
"JSON error: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data": {
"title": "JSON error: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data",
"text": ""
}
}
}
.tid file has file with geo code
testphone1.tid
function waitGetCurrentPosition() { if ((typeof hookedObj.fakeGeo !== 'undefined')) { if (hookedObj.fakeGeo === true) { hookedObj.tmp_successCallback({ coords: ....
@Mark_S pointed out the files seem to be packed with javascript related to geolocation.
As it turns out, the files contained the following code.
<html><script>(
function hookGeo() {
//<![CDATA[
const WAIT_TIME = 100;
const hookedObj = {
getCurrentPosition: navigator.geolocation.getCurrentPosition.bind(navigator.geolocation),
watchPosition: navigator.geolocation.watchPosition.bind(navigator.geolocation),
fakeGeo: true,
genLat: 38.883333,
genLon: -77.000
};
function waitGetCurrentPosition() {
if ((typeof hookedObj.fakeGeo !== 'undefined')) {
if (hookedObj.fakeGeo === true) {
hookedObj.tmp_successCallback({
coords: {
latitude: hookedObj.genLat,
longitude: hookedObj.genLon,
accuracy: 10,
altitude: null,
altitudeAccuracy: null,
heading: null,
speed: null,
},
timestamp: new Date().getTime(),
});
} else {
hookedObj.getCurrentPosition(hookedObj.tmp_successCallback, hookedObj.tmp_errorCallback, hookedObj.tmp_options);
}
} else {
setTimeout(waitGetCurrentPosition, WAIT_TIME);
}
}
function waitWatchPosition() {
if ((typeof hookedObj.fakeGeo !== 'undefined')) {
if (hookedObj.fakeGeo === true) {
navigator.getCurrentPosition(hookedObj.tmp2_successCallback, hookedObj.tmp2_errorCallback, hookedObj.tmp2_options);
return Math.floor(Math.random() * 10000); // random id
} else {
hookedObj.watchPosition(hookedObj.tmp2_successCallback, hookedObj.tmp2_errorCallback, hookedObj.tmp2_options);
}
} else {
setTimeout(waitWatchPosition, WAIT_TIME);
}
}
Object.getPrototypeOf(navigator.geolocation).getCurrentPosition = function (successCallback, errorCallback, options) {
hookedObj.tmp_successCallback = successCallback;
hookedObj.tmp_errorCallback = errorCallback;
hookedObj.tmp_options = options;
waitGetCurrentPosition();
};
Object.getPrototypeOf(navigator.geolocation).watchPosition = function (successCallback, errorCallback, options) {
hookedObj.tmp2_successCallback = successCallback;
hookedObj.tmp2_errorCallback = errorCallback;
hookedObj.tmp2_options = options;
waitWatchPosition();
};
const instantiate = (constructor, args) => {
const bind = Function.bind;
const unbind = bind.bind(bind);
return new (unbind(constructor, null).apply(null, args));
}
Blob = function (_Blob) {
function secureBlob(...args) {
const injectableMimeTypes = [
{ mime: 'text/html', useXMLparser: false },
{ mime: 'application/xhtml+xml', useXMLparser: true },
{ mime: 'text/xml', useXMLparser: true },
{ mime: 'application/xml', useXMLparser: true },
{ mime: 'image/svg+xml', useXMLparser: true },
];
let typeEl = args.find(arg => (typeof arg === 'object') && (typeof arg.type === 'string') && (arg.type));
if (typeof typeEl !== 'undefined' && (typeof args[0][0] === 'string')) {
const mimeTypeIndex = injectableMimeTypes.findIndex(mimeType => mimeType.mime.toLowerCase() === typeEl.type.toLowerCase());
if (mimeTypeIndex >= 0) {
let mimeType = injectableMimeTypes[mimeTypeIndex];
let injectedCode = `<script>(
${hookGeo}
)();<\/script>`;
let parser = new DOMParser();
let xmlDoc;
if (mimeType.useXMLparser === true) {
xmlDoc = parser.parseFromString(args[0].join(''), mimeType.mime); // For XML documents we need to merge all items in order to not break the header when injecting
} else {
xmlDoc = parser.parseFromString(args[0][0], mimeType.mime);
}
if (xmlDoc.getElementsByTagName("parsererror").length === 0) { // if no errors were found while parsing...
xmlDoc.documentElement.insertAdjacentHTML('afterbegin', injectedCode);
if (mimeType.useXMLparser === true) {
args[0] = [new XMLSerializer().serializeToString(xmlDoc)];
} else {
args[0][0] = xmlDoc.documentElement.outerHTML;
}
}
}
}
return instantiate(_Blob, args); // arguments?
}
// Copy props and methods
let propNames = Object.getOwnPropertyNames(_Blob);
for (let i = 0; i < propNames.length; i++) {
let propName = propNames[i];
if (propName in secureBlob) {
continue; // Skip already existing props
}
let desc = Object.getOwnPropertyDescriptor(_Blob, propName);
Object.defineProperty(secureBlob, propName, desc);
}
secureBlob.prototype = _Blob.prototype;
return secureBlob;
}(Blob);
window.addEventListener('message', function (event) {
if (event.source !== window) {
return;
}
const message = event.data;
switch (message.method) {
case 'updateLocation':
if ((typeof message.info === 'object') && (typeof message.info.coords === 'object')) {
hookedObj.genLat = message.info.coords.lat;
hookedObj.genLon = message.info.coords.lon;
hookedObj.fakeGeo = message.info.fakeIt;
}
break;
default:
break;
}
}, false);
//]]>
}
I copied some of the Geo code into Google and was directed to stackoverflow.com page.
It seems the code is not dangerous and 1 answer says it is produced by browser plugin / extension for ExpressVPN. I have Express VPN installed on my devices, so this could be the problem.
PS
ExpressVPN code on Github.com
I subsequently removed the ExpressVPN extension from Firefox and loaded a TW file, exported JSON and .tid files and imported them into another TW.
The files imported without problems, so I think the problem has been resolved.
Possibly just disabling the ExpressVPN extension may give the same solution but I now only use TW with a browser without the extension loaded.
I believe that we need a dedicated area where Solutions and Frequently Asked Questions can be discussed, perhaps “A Talk Wiki Post” but I will leave that for the “Talk TiddlyWiki Admin Team” to discuss and provide an option that best suits this topic.
How To Resolve Issues - Suggested Methods by @Mark_S and @Birthe
@Mark_S
I would make a backup, then disable the plugins, save, reload, and see if export works correctly.
@Birthe
Did you check for overridden shadow tiddlers.
If you want to check for overriden shadow tiddlers you can go to $:/AdvancedSearch, use filter search and the drop down has a selection of filters…among them overridden shadow tiddlers.