Преглед изворни кода

fix: encode webRequest's responseHeaders for GMxhr (#1330)

tophf пре 4 година
родитељ
комит
18c4c41189
2 измењених фајлова са 37 додато и 7 уклоњено
  1. 25 5
      src/background/utils/requests.js
  2. 12 2
      src/common/util.js

+ 25 - 5
src/background/utils/requests.js

@@ -10,6 +10,7 @@ const VM_VERIFY = 'VM-Verify';
 const requests = {};
 const verify = {};
 const tabRequests = {};
+let encoder;
 
 Object.assign(commands, {
   ConfirmInstall: confirmInstall,
@@ -163,11 +164,7 @@ const HeaderInjector = (() => {
               || setCookieInStore(h.value, req, url)
             ));
           }
-          // mimic https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/getAllResponseHeaders
-          req.responseHeaders = headers
-          .map(({ name, value }) => `${name}: ${value}\r\n`)
-          .sort()
-          .join('');
+          req.responseHeaders = headers.map(encodeWebRequestHeader).join('');
           return { responseHeaders: headers };
         }
       },
@@ -540,3 +537,26 @@ export function clearRequestsByTabId(tabId) {
     }
   }
 }
+
+/**
+ * Imitating https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/getAllResponseHeaders
+ * Per the specification https://tools.ietf.org/html/rfc7230 the header name is within ASCII,
+ * but we'll try encoding it, if necessary, to handle invalid server responses.
+ */
+function encodeWebRequestHeader({ name, value, binaryValue }) {
+  return `${string2byteString(name)}: ${
+    binaryValue
+      ? buffer2string(binaryValue)
+      : string2byteString(value)
+  }\r\n`;
+}
+
+/**
+ * Returns a UTF8-encoded binary string i.e. one byte per character.
+ * Returns the original string in case it was already within ASCII.
+ */
+function string2byteString(str) {
+  if (!/[\u0080-\uFFFF]/.test(str)) return str;
+  if (!encoder) encoder = new TextEncoder();
+  return buffer2string(encoder.encode(str));
+}

+ 12 - 2
src/common/util.js

@@ -79,14 +79,24 @@ export function getUniqId(prefix = 'VM') {
     + floor(random() * 1e12)::numberToString(36);
 }
 
+/**
+ * @param {ArrayBuffer|Uint8Array|Array} buf
+ * @param {number} [offset]
+ * @param {number} [length]
+ * @return {string} a binary string i.e. one byte per character
+ */
 export function buffer2string(buf, offset = 0, length = 1e99) {
   // The max number of arguments varies between JS engines but it's >32k so we're safe
   const sliceSize = 8192;
   const slices = [];
-  const end = Math.min(buf.byteLength, offset + length);
+  const arrayLen = buf.length; // present on Uint8Array/Array
+  const end = Math.min(arrayLen || buf.byteLength, offset + length);
+  const needsSlicing = arrayLen == null || offset || end > sliceSize;
   for (; offset < end; offset += sliceSize) {
     slices.push(String.fromCharCode.apply(null,
-      new Uint8Array(buf, offset, Math.min(sliceSize, end - offset))));
+      needsSlicing
+        ? new Uint8Array(buf, offset, Math.min(sliceSize, end - offset))
+        : buf));
   }
   return slices.join('');
 }