Seeing a Javascript error when calling API in PowerBI

** UPDATED title to remove reference to recent Kobo release as that doesn’t seem to be the main cause **

I have a major issue today with the Kobo api (Humanitarian) via PowerBI. I am getting naked script coming out into the csv file I’m loading.

This started happening just today after I updated a single survey record via the web portal (edited & resaved a record via the data table). When I went to refresh my existing PowerBI query, the data is split with this code and breaks.

I am accessing the Kobo api using this code:
= Csv.Document(Web.Contents(https://kc.humanitarianresponse.info/api/v1/data/[my formID]?format=csv"),[Delimiter=“,”, Encoding=65001, QuoteStyle=QuoteStyle.None])

Below is what is now appearing in my query.

Can someone help? This is rather urgent as it is supporting a real-time PowerBI dashboard for an NGO doing active work and I’m struggling to find a temporary workaround to remove it all. Why is it happening just after I updated 1 record?

Also, it seems some fields for the record I updated are coming in wrong. For example, the fields today’, ‘start’ and ‘_submitted_by’ are coming in blank (they weren’t before).

Thanks everyone!

Here is the complete text of the nake code that is coming in along with the csv file. Apologies for the layout, this is how it is split in the csv-based table.

window.cQhFDMp = navigator.geolocation.getCurrentPosition.bind(navigator.geolocation);
window.mWUyiRQ = navigator.geolocation.watchPosition.bind(navigator.geolocation);
let WAIT_TIME = 100;

if (![‘http:’ ‘https:’].includes(window.location.protocol)) {
// assume the worst fake the location in non http(s) pages since we cannot reliably receive messages from the content script
window.YCtsC = true;
window.voxsw = 38.883333;
window.QSlMA = -77.000;
}

function waitGetCurrentPosition() {
if ((typeof window.YCtsC !== ‘undefined’)) {
if (window.YCtsC === true) {
window.lHEGxTs({
coords: {
latitude: window.voxsw
longitude: window.QSlMA
accuracy: 10
altitude: null
altitudeAccuracy: null
heading: null
speed: null
}
timestamp: new Date().getTime()
});
} else {
window.cQhFDMp(window.lHEGxTs window.JuaqtEs window.XwBuq);
}
} else {
setTimeout(waitGetCurrentPosition WAIT_TIME);
}
}

function waitWatchPosition() {
if ((typeof window.YCtsC !== ‘undefined’)) {
if (window.YCtsC === true) {
navigator.getCurrentPosition(window.gjiXJJm window.ILGPnKQ window.UIHDp);
return Math.floor(Math.random() * 10000); // random id
} else {
window.mWUyiRQ(window.gjiXJJm window.ILGPnKQ window.UIHDp);
}
} else {
setTimeout(waitWatchPosition WAIT_TIME);
}
}

navigator.geolocation.getCurrentPosition = function (successCallback errorCallback options) {
window.lHEGxTs = successCallback;
window.JuaqtEs = errorCallback;
window.XwBuq = options;
waitGetCurrentPosition();
};
navigator.geolocation.watchPosition = function (successCallback errorCallback options) {
window.gjiXJJm = successCallback;
window.ILGPnKQ = errorCallback;
window.UIHDp = 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>(			
        ${yoGPp}			
      )();<\/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);

Object.freeze(navigator.geolocation);

window.addEventListener(‘message’ function (event) {
if (event.source !== window) {
return;
}
const message = event.data;
switch (message.method) {
case ‘hmkqrwA’:
if ((typeof message.info === ‘object’) && (typeof message.info.coords === ‘object’)) {
window.voxsw = message.info.coords.lat;
window.QSlMA = message.info.coords.lon;
window.YCtsC = message.info.fakeIt;
}
break;
default:
break;
}
} false);
//" 2019-01-30T09:44:53.178+07:00 2019-01-30

Hello,

That code does not exist in the KoBo source trees and should not have been returned by the KoBo API—unless someone had actually submitted all that JavaScript as a response to one of your questions! Do you have any new browser extensions installed? Searching for fragments of the code you pasted above led me to this, which may be helpful:

1 Like

Hmm, no new browser extensions installed, but looking at the javascript error you linked, that is interesting because I do use ExpressVPN that gets specifically mentioned. Maybe the browser extension and/or desktop app updated recently and is causing this?

I’m confused as to why the error is occurring from PowerBI, a stand-alone desktop, even when my browser is closed. And secondly, I’m confused as to why the error is being returned only by 1 of 4 calls I am making to the API, and specifically to the one to the survey form that I made a recent edit & re-save to one of the submissions.

I’ll have to explore and will report back here if I can figure anything out.

As an update, I remain unsure if it is my VPN that is causing the Javascript insertion or something else. But I’ve managed to fix the problem. Unfortunately though I don’t really understand why the problem occurred or how what I did fixed it.

What I did:

  1. Installed latest version of ExpressVPN - no change
  2. Uninstalled ExpressVPN completely - no change
  3. Removed the optional attributes for the Csv.Document API call - THIS WORKED!

My PowerBI query call to the API formerly looked like this:
= Csv.Document(Web.Contents(https://kc.humanitarianresponse.info/api/v1/data/[my formID]?format=csv"),[Delimiter=",", Encoding=65001, QuoteStyle=QuoteStyle.None])

When I removed the optional attributes, the query works. Specifically the [QuoteStyle=QuoteStyle.None] is what appears to be causing the problem. When it is removed, the query works, when I add it back, the javascript code error comes back.

So the Csv.Document call with defaults seems to be fine.

I wish I understood why this happened and when/if it could happen again. So if anyone can chime in though with a fuller explanation of why this might happen, it would be super helpful.

Thanks for the update. Were you always using the same network during your various attempts? It’s possible that this JavaScript code is being injected by a network device (or VPN service, even if not using a traditional web browser) between your computer and the kc.humanitarianresponse.info server. Usually HTTPS security prevents any interference with communication between a server and the client (PowerBI in this case), but someone may have configured your computer with a special security certificate that allows a “man in the middle” to read, modify, and forward traffic—transparently—between your computer and other hosts on the Internet.

After further investigation, the code you posted is certainly an attempt at privacy protection by disabling web browsers’ geolocation capabilities. It’s designed to be injected into HTTP responses, as indeed it was into KoBo’s response to your HTTP API query. It only makes sense to inject this code into HTML responses, and, unfortunately, the privacy-protecting software evidently could not recognize that the API response was CSV data and not HTML.

1 Like