UNPKG

@zkp2p/reclaim-witness-sdk

Version:

<div> <div> <img src="https://raw.githubusercontent.com/reclaimprotocol/.github/main/assets/banners/Attestor-Core.png" /> </div> </div>

1,047 lines (1,046 loc) 270 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const tls_1 = require("@reclaimprotocol/tls"); const assert_1 = __importDefault(require("assert")); const config_1 = require("../config"); const providers_1 = require("../providers"); const http_1 = __importDefault(require("../providers/http")); const utils_1 = require("../providers/http/utils"); const test_http_parser_1 = require("../tests/test.http-parser"); const utils_2 = require("../utils"); const v8_1 = require("v8"); jest.setTimeout(60000); const ctx = config_1.PROVIDER_CTX; describe('HTTP Provider Utils tests', () => { const { hostPort, geoLocation, getResponseRedactions, createRequest, assertValidProviderReceipt } = providers_1.providers['http']; const transcript = JSON.parse('[' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio=","sender":"server"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg==","sender":"server"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq","sender":"server"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg==","sender":"server"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio=","sender":"server"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKio=","sender":"server"},' + '{"message":"R0VUIC8gSFRUUC8xLjENCkhvc3Q6IHhhcmdzLm9yZw0KQ29udGVudC1MZW5ndGg6IDQNCkNvbm5lY3Rpb246IGNsb3NlDQpBY2NlcHQtRW5jb2Rpbmc6IGlkZW50aXR5DQp1c2VyLWFnZW50OiBNb3ppbGxhLzUuMA0K","sender":"client"},' + '{"message":"KioqKio=","sender":"client"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg==","sender":"client"},' + '{"message":"KioqKio=","sender":"client"},' + '{"message":"DQoNCnQ=","sender":"client"},' + '{"message":"KioqKio=","sender":"client"},' + '{"message":"Kg==","sender":"client"},' + '{"message":"KioqKio=","sender":"client"},' + '{"message":"c3Q=","sender":"client"},' + '{"message":"SFRUUC8xLjEgMjAwIE9LKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KDQo=","sender":"server"},' + '{"message":"KioqKioqKioqKioqKioqKioqKioqKioqKioqKjx0aXRsZT5BaWtlbiAmYW1wOyBEcmlzY29sbCAmYW1wOyBXZWJiPC90aXRsZT4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqT25lIG9mIHRoZSBmZXcgZXhjZXB0aW9ucyBpcyBhIHNlcmllcyBvZiBkb2N1bWVudHMgdGhhdCBJJ3ZlIHdyaXR0ZW4KICAgIGJyZWFraW5nIGRvd24gY3J5cHRvZ3JhcGhpYyBhbmQgbmV0d29yayBwcm90b2NvbHMgYnl0ZS1ieS1ieXRlLiBJJ20KICAgIGFsd2F5cyBoZWFyaW5nIGZyb20gdGVhY2hlcnMsIHN0dWRlbnRzLCBhbmQgZmVsbG93IHNvZnR3YXJlIGRldmVsb3BlcnMKICAgIHdobyB1c2UgdGhlc2UgdG8gbGVhcm4sIHRvIGZpeCwgYW5kIHRvIHVuZGVyc3RhbmQuIEknbSB2ZXJ5IHByb3VkIG9mIHRoYXQuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq","sender":"server"},{"message":"Kio=","sender":"server"}]') .map((x) => ({ ...x, message: Buffer.from(x.message, 'base64'), })); it('should parse xpath & JSON path', () => { const json = (0, utils_1.extractHTMLElement)(html, "//script[@data-component-name='Navbar']", true); const val = (0, utils_1.extractJSONValueIndex)(json, '$.hasBookface'); const rm = '"hasBookface":true'; const regexp = new RegExp(rm, 'gim'); expect(regexp.test(json.slice(val.start, val.end))).toBe(true); }); it('should extract complex JSON path', () => { const json = `{ "items":[ { "name": "John Doe", "country": "USA" }, { "country": "USA", "age":25 } ] }`; const val = (0, utils_1.extractJSONValueIndex)(json, '$.items[?(@.name.match(/.*oe/))].name'); const rm = '"name": "John Doe"'; const regexp = new RegExp(rm, 'gim'); expect(regexp.test(json.slice(val.start, val.end))).toBe(true); }); it('should get inner & outer tag contents', () => { const html = `<body> <div id="content123">This is <span>some</span> text!</div> <div id="content456">This is <span>some</span> other text!</div> <div id="content789">This is <span>some</span> irrelevant text!</div> </body>`; let content = (0, utils_1.extractHTMLElement)(html, "//div[contains(@id, 'content123')]", true); expect(content).toEqual('This is <span>some</span> text!'); content = (0, utils_1.extractHTMLElement)(html, "//div[contains(@id, 'content456')]", false); expect(content).toEqual('<div id="content456">This is <span>some</span> other text!</div>'); }); it('should get multiple elements', () => { const html = `<body> <div id="content123">This is <span>some</span> text!</div> <div id="content456">This is <span>some</span> other text!</div> <div id="content789">This is <span>some</span> irrelevant text!</div> </body>`; const contents = (0, utils_1.extractHTMLElements)(html, '//body/div', true); expect(contents).toEqual(['This is <span>some</span> text!', 'This is <span>some</span> other text!', 'This is <span>some</span> irrelevant text!']); }); it('should get multiple JSONPaths', () => { const jsonData = `{ "firstName": "John", "lastName": "doe", "age": 26, "address": { "streetAddress": "naist street", "city": "Nara", "postalCode": "630-0192" }, "phoneNumbers": [ { "type": "iPhone", "number": "0123-4567-8888" }, { "type": "home", "number": "0123-4567-8910" } ] }`; const contents = (0, utils_1.extractJSONValueIndexes)(jsonData, '$.phoneNumbers[*].number'); const res = []; for (const { start, end } of contents) { res.push(jsonData.slice(start, end)); } expect(res).toEqual(['"number": "0123-4567-8888"', '"number": "0123-4567-8910"']); }); it('should error on incorrect jsonPath', () => { expect(() => { (0, utils_1.extractJSONValueIndex)(('{"asdf": 1}'), '(alert(origin))'); }).toThrow('loc.indexOf is not a function'); }); it('should not error on incorrect regex', () => { expect(() => { const regexp = (0, utils_1.makeRegex)('([a-z]+)+$'); regexp.test('a'.repeat(31) + '\x00'); }).not.toThrow(); }); it('should hide chunked parts from response', () => { const provider = http_1.default; const simpleChunk = Buffer.from('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n9\r\nchunk 1, \r\n7\r\nchunk 2\r\n0\r\n'); if (provider.getResponseRedactions) { const redactions = provider.getResponseRedactions({ response: simpleChunk, params: { method: 'GET', url: 'https://test.com', 'responseMatches': [], 'responseRedactions': [ { 'regex': 'chunk 1, chunk 2' } ], }, logger: utils_2.logger, ctx }); expect(redactions).toEqual([ { 'fromIndex': 15, 'toIndex': 88, }, { 'fromIndex': 92, 'toIndex': 95 }, { 'fromIndex': 104, 'toIndex': 109 }, { 'fromIndex': 116, 'toIndex': 121 } ]); let start = 0; let str = ''; for (const red of redactions) { str += simpleChunk.subarray(start, red.fromIndex); start = red.toIndex; } expect(str).toEqual('HTTP/1.1 200 OK\r\n\r\nchunk 1, chunk 2'); } }); it('should perform complex redactions', () => { const provider = http_1.default; const response = Buffer.from('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\ncontent-length: 222\r\nConnection: close\r\n\r\n<body> <div id="c1">{"ages":[{"age":"26"},{"age":"27"},{"age":"28"}]}</div> <div id="c2">{"ages":[{"age":"27"},{"age":"28"},{"age":"29"}]}</div> <div id="c3">{"ages":[{"age":"29"},{"age":"30"},{"age":"31"}]}</div></body>\r\n'); if (provider.getResponseRedactions) { const redactions = provider.getResponseRedactions({ response, params: { method: 'GET', url: 'https://test.com', 'responseMatches': [], 'responseRedactions': [ { 'xPath': '//body/div', 'jsonPath': '$.ages[*].age', 'regex': '(2|3)\\d' } ], }, logger: utils_2.logger, ctx, }); expect(redactions).toEqual([ { 'fromIndex': 15, 'toIndex': 81, }, { 'fromIndex': 85, 'toIndex': 122 }, { 'fromIndex': 124, 'toIndex': 135 }, { 'fromIndex': 137, 'toIndex': 148 }, { 'fromIndex': 150, 'toIndex': 191 }, { 'fromIndex': 193, 'toIndex': 204 }, { 'fromIndex': 206, 'toIndex': 217 }, { 'fromIndex': 219, 'toIndex': 260 }, { 'fromIndex': 262, 'toIndex': 273 }, { 'fromIndex': 275, 'toIndex': 286 }, { 'fromIndex': 288, 'toIndex': 307 } ]); let start = 0; let str = ''; for (const red of redactions) { str += response.subarray(start, red.fromIndex); start = red.toIndex; } expect(str).toEqual('HTTP/1.1 200 OK\r\n\r\n262728272829293031'); } }); it('should perform complex redactions 2', () => { const provider = http_1.default; const response = Buffer.from('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\ncontent-length: 51\r\nConnection: close\r\n\r\n{"ages":[{"age":"26"},{"age":"27"},{"age":"28"}]}\r\n'); if (provider.getResponseRedactions) { const redactions = provider.getResponseRedactions({ response, params: { method: 'GET', url: 'https://test.com', 'responseMatches': [], 'responseRedactions': [ { 'jsonPath': '$.ages[*].age', 'regex': '(2|3)\\d' } ], }, logger: utils_2.logger, ctx, }); expect(redactions).toEqual([ { 'fromIndex': 15, 'toIndex': 80, }, { 'fromIndex': 84, 'toIndex': 101 }, { 'fromIndex': 103, 'toIndex': 114 }, { 'fromIndex': 116, 'toIndex': 127 }, { 'fromIndex': 129, 'toIndex': 135 } ]); let start = 0; let str = ''; for (const red of redactions) { str += response.subarray(start, red.fromIndex); start = red.toIndex; } expect(str).toEqual('HTTP/1.1 200 OK\r\n\r\n262728'); } }); it('should perform complex redactions 3', () => { const provider = http_1.default; const response = Buffer.from('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\ncontent-length: 222\r\nConnection: close\r\n\r\n<body> <div id="c1">{"ages":[{"age":"26"},{"age":"27"},{"age":"28"}]}</div> <div id="c2">{"ages":[{"age":"27"},{"age":"28"},{"age":"29"}]}</div> <div id="c3">{"ages":[{"age":"29"},{"age":"30"},{"age":"31"}]}</div></body>\r\n'); if (provider.getResponseRedactions) { const redactions = provider.getResponseRedactions({ response, params: { method: 'GET', url: 'https://test.com', 'responseMatches': [], 'responseRedactions': [ { 'xPath': '//body/div', 'regex': '"age":"\\d{2}"' } ], }, logger: utils_2.logger, ctx, }); expect(redactions).toEqual([ { 'fromIndex': 15, 'toIndex': 81, }, { 'fromIndex': 85, 'toIndex': 115 }, { 'fromIndex': 125, 'toIndex': 184 }, { 'fromIndex': 194, 'toIndex': 253 }, { 'fromIndex': 263, 'toIndex': 307 } ]); let start = 0; let str = ''; for (const red of redactions) { str += response.subarray(start, red.fromIndex); start = red.toIndex; } expect(str).toEqual('HTTP/1.1 200 OK\r\n\r\n"age":"26""age":"27""age":"29"'); } }); it('should get redactions from chunked response', () => { const provider = http_1.default; if (provider.getResponseRedactions) { const redactions = provider.getResponseRedactions({ response: chunkedResp, params: { method: 'GET', url: 'https://bookface.ycombinator.com/home', 'responseMatches': [], 'responseRedactions': [ { 'xPath': "//script[@id='js-react-on-rails-context']", 'jsonPath': '$.currentUser', }, { 'xPath': "//script[@data-component-name='BookfaceCsrApp']", 'jsonPath': '$.hasBookface', }, { 'regex': 'code_version:\\s"[0-9a-f]{40}\\sruby' } ], }, logger: utils_2.logger, ctx, }); expect(redactions).toEqual([ { 'fromIndex': 15, 'toIndex': 17 }, { 'fromIndex': 52, 'toIndex': 4290, }, { 'fromIndex': 4294, 'toIndex': 4760 }, { 'fromIndex': 4820, 'toIndex': 53268 }, { 'fromIndex': 53507, 'toIndex': 58705 }, { 'fromIndex': 58723, 'toIndex': 64093 } ]); } }); it('should hash provider params consistently', () => { const params = { url: 'https://xargs.org/', responseMatches: [ { type: 'regex', value: '<title.*?(?<name>Aiken &amp; Driscoll &amp; Webb)<\\/title>' } ], method: 'GET', responseRedactions: [{ xPath: './html/head/title' }], geoLocation: 'US', }; const hash = (0, utils_2.hashProviderParams)(params); expect(hash).toEqual('0xe9624d26421a4d898d401e98821ccd645c25b06de97746a6c24a8b12d9aec143'); const paramsEx = { 'geoLocation': '', 'url': 'https://www.linkedin.com/dashboard/', 'method': 'GET', 'body': '', 'responseMatches': [ { 'value': 'TOTAL_FOLLOWERS&quot;,&quot;$recipeTypes&quot;:[&quot;com.linkedin.c123aee2ba3dfeb6a4580e7effdf5d3f&quot;],&quot;analyticsTitle&quot;:{&quot;textDirection&quot;:&quot;USER_LOCALE&quot;,&quot;text&quot;:&quot;581&quot;', 'type': 'contains' } ], 'responseRedactions': [{ 'xPath': '{{xpath}}', 'jsonPath': '', 'regex': 'TOTAL_FOLLOWERS&quot;,&quot;\\$recipeTypes&quot;:(.*?),&quot;analyticsTitle&quot;:{&quot;textDirection&quot;:&quot;USER_LOCALE&quot;,&quot;text&quot;:&quot;(.*?)&quot;' }] }; expect((0, utils_2.hashProviderParams)(paramsEx)).toEqual('0x6fb81ebab0fb5dca0356abfd8726af97675e4a426712377bfc6ad9a0271c913b'); }); it('should match redacted strings', () => { const testCases = [ { a: 'aaa', b: 'aaa' }, { a: '{{abc}}', b: '************' }, { a: '{{abc}}d', b: '*d' }, { a: 'd{{abc}}', b: 'd*******************************************' }, { a: 'd{{abc}}d{{abwewewewec}}', b: 'd*d*' }, { a: '{{abc}}x{{abwewewewec}}', b: '*x*' } ]; for (const { a, b } of testCases) { expect((0, utils_1.matchRedactedStrings)((0, tls_1.strToUint8Array)(a), (0, tls_1.strToUint8Array)(b))).toBeTruthy(); } }); it('should not match bad redacted strings', () => { const testCases = [ { a: 'aaa', b: 'aab' }, { a: '{{abc}}', b: '' }, { a: '', b: '*****' }, { a: '{{abc}}{{abc}}d', b: '*d' }, { a: '{{yy', b: '*' }, { a: '{{abc}}d{{abwewewewec}}', b: 'a*d*' }, { a: '{abc}}', b: '************' } ]; for (const { a, b } of testCases) { expect((0, utils_1.matchRedactedStrings)((0, tls_1.strToUint8Array)(a), (0, tls_1.strToUint8Array)(b))).toBeFalsy(); } }); it('should throw on invalid URL', () => { expect(() => ((0, utils_2.getProviderValue)({ url: 'abc', responseMatches: [], responseRedactions: [], method: 'GET' }, hostPort))).toThrow('Invalid URL'); }); it('should throw on invalid params', () => { expect(() => { (0, utils_2.assertValidateProviderParams)('http', { a: 'b', body: 2 }); }).toThrow(/^Params validation failed/); }); it('should throw on invalid secret params', () => { expect(() => { createRequest({ cookieStr: undefined, authorisationHeader: undefined, headers: undefined }, { url: 'abc', responseMatches: [], responseRedactions: [], method: 'GET' }, utils_2.logger); }).toThrow('auth parameters are not set'); }); it('should return empty redactions', () => { const res = `HTTP/1.1 200 OK\r Content-Length: 0\r Connection: close\r Content-Type: text/html; charset=utf-8\r \r `; const redactions = (getResponseRedactions) ? getResponseRedactions({ response: (0, tls_1.strToUint8Array)(res), params: { url: 'abc', responseMatches: [], responseRedactions: [], method: 'GET' }, logger: utils_2.logger, ctx }) : undefined; expect(redactions).toHaveLength(0); }); it('should throw on empty body', () => { const res = `HTTP/1.1 200 OK\r Content-Length: 0\r Connection: close\r Content-Type: text/html; charset=utf-8\r \r `; expect(() => { if (getResponseRedactions) { getResponseRedactions({ response: (0, tls_1.strToUint8Array)(res), params: { url: 'abc', responseMatches: [], responseRedactions: [{ regex: 'abc' }], method: 'GET' }, logger: utils_2.logger, ctx, }); } }).toThrow('Failed to find response body'); }); it('should throw on bad xpath', () => { const res = `HTTP/1.1 200 OK\r Content-Length: 1\r Connection: close\r Content-Type: text/html; charset=utf-8\r \r 1`; expect(() => { if (getResponseRedactions) { getResponseRedactions({ response: (0, tls_1.strToUint8Array)(res), params: { url: 'abc', responseMatches: [], responseRedactions: [{ xPath: 'abc' }], method: 'GET' }, logger: utils_2.logger, ctx }); } }).toThrow('Failed to find XPath: \"abc\"'); }); it('should throw on bad jsonPath', () => { const res = `HTTP/1.1 200 OK\r Content-Length: 1\r Connection: close\r Content-Type: text/html; charset=utf-8\r \r 1`; expect(() => { if (getResponseRedactions) { getResponseRedactions({ response: (0, tls_1.strToUint8Array)(res), params: { url: 'abc', responseMatches: [], responseRedactions: [{ jsonPath: 'abc' }], method: 'GET' }, logger: utils_2.logger, ctx, }); } }).toThrow('jsonPath not found'); }); it('should throw on bad regex', () => { const res = `HTTP/1.1 200 OK\r Content-Length: 1\r Connection: close\r Content-Type: text/html; charset=utf-8\r \r 1`; expect(() => { if (getResponseRedactions) { getResponseRedactions({ response: (0, tls_1.strToUint8Array)(res), params: { url: 'abc', responseMatches: [], responseRedactions: [{ regex: 'abc' }], method: 'GET' }, logger: utils_2.logger, ctx, }); } }).toThrow('regexp abc does not match found element \'MQ==\''); }); it('should throw on bad method', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'abc', responseMatches: [], responseRedactions: [], method: 'POST' }, logger: utils_2.logger, ctx }); }).rejects.toThrow('Invalid method: get'); }); it('should throw on bad protocol', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'http://xargs.com', responseMatches: [], responseRedactions: [], method: 'GET' }, logger: utils_2.logger, ctx, }); }).rejects.toThrow('Expected protocol: https, found: http:'); }); it('should throw on duplicate groups', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://xargs.{{abc}}', responseMatches: [{ type: 'regex', value: '(?<abc>.)' }], responseRedactions: [], method: 'GET', paramValues: { 'abc': 'org' } }, logger: utils_2.logger, ctx }); }).rejects.toThrow('Duplicate parameter abc'); }); it('should throw on bad path', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://xargs.com/abc', responseMatches: [], responseRedactions: [], method: 'GET' }, logger: utils_2.logger, ctx }); }).rejects.toThrow('Expected path: /abc, found: /'); }); it('should throw on bad host', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://abc.com/', responseMatches: [], responseRedactions: [], method: 'GET' }, logger: utils_2.logger, ctx, }); }).rejects.toThrow('Expected host: abc.com, found: xargs.org'); }); it('should throw on bad OK string', async () => { const temp = cloneObject(transcript); // changes the status ("OK") text to something else // it'll be in the first server response packet const firstServerMsg = temp.find((x, index) => x.sender === 'server' && index !== 0); firstServerMsg.message[0] = 32; await expect(async () => { await assertValidProviderReceipt({ receipt: temp, params: { url: 'https://xargs.org/', responseMatches: [], responseRedactions: [], method: 'GET' }, logger: utils_2.logger, ctx, }); }).rejects.toThrow('Response did not start with \"HTTP/1.1 2XX\"'); }); it('should throw on bad close header', async () => { const temp = cloneObject(transcript); const clientMsgWithClose = temp.find((x) => { if (x.sender !== 'client') { return false; } return (0, utils_2.uint8ArrayToStr)(x.message) .includes('Connection: close'); }); clientMsgWithClose.message[68] = 102; await expect(async () => { await assertValidProviderReceipt({ receipt: temp, params: { url: 'https://xargs.org/', responseMatches: [], responseRedactions: [], method: 'GET' }, logger: utils_2.logger, ctx }); }).rejects.toThrow('Connection header must be \"close\"'); }); it('should throw on bad body', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://xargs.org/', responseMatches: [], responseRedactions: [], method: 'GET', body: 'abc' }, logger: utils_2.logger, ctx }); }).rejects.toThrow('request body mismatch'); }); it('should throw on bad regex match', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://xargs.org/', responseMatches: [{ type: 'regex', value: 'abc' }], responseRedactions: [], method: 'GET', }, logger: utils_2.logger, ctx }); }).rejects.toThrow('Invalid receipt. Regex \"abc\" didn\'t match'); }); it('should throw on bad contains match', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://xargs.org/', responseMatches: [{ type: 'contains', value: 'abc' }], responseRedactions: [], method: 'GET', }, logger: utils_2.logger, ctx, }); }).rejects.toThrow('Invalid receipt. Response does not contain \"abc\"'); }); it('should get geo', () => { const geo = (0, utils_2.getProviderValue)({ geoLocation: '{{geo}}', paramValues: { 'geo': 'US' } }, geoLocation); expect(geo).toEqual('US'); }); it('should throw on bad geo param', () => { expect(() => { // @ts-ignore geoLocation({ geoLocation: '{{geo}}', paramValues: { 'geo1': 'US' } }); }).toThrow('parameter "geo" value not found in templateParams'); }); it('should return empty geo', () => { expect(// @ts-ignore geoLocation({ geoLocation: '', })).toEqual(undefined); }); it('should throw on bad param in url', () => { expect(() => { // @ts-ignore return hostPort({ url: 'https://xargs.{{param1}}' }); }) .toThrow('parameter "param1" value not found in templateParams'); }); it('should throw on bad url', () => { expect(() => { // @ts-ignore hostPort({ url: 'file:///C:/path' }); }) .toThrow('url is incorrect'); }); it('should throw on bad match type', async () => { await expect(async () => { const params = { url: 'https://xargs.org/', responseMatches: [{ type: 'abc', value: 'abc' }], responseRedactions: [], method: 'GET', }; await assertValidProviderReceipt({ receipt: transcript, // @ts-ignore params, logger: utils_2.logger, ctx, }); }).rejects.toThrow('Invalid response match type abc'); }); it('should throw on no non present params', async () => { await expect(async () => { await assertValidProviderReceipt({ receipt: transcript, params: { url: 'https://xargs.{{org}}/', responseMatches: [{ type: 'contains', value: 'abc' }], responseRedactions: [], method: 'GET', }, logger: utils_2.logger, ctx }); }).rejects.toThrow('Expected host: xargs.{{org}}, found: xargs.org'); }); it('should throw on non present secret params', () => { expect(() => { createRequest({ cookieStr: 'abc', }, { url: 'https://xargs.{{com}}', responseMatches: [], responseRedactions: [], method: 'GET' }, utils_2.logger); }).toThrow('parameter\'s \"com\" value not found in paramValues and secret parameter\'s paramValues'); }); it('should replace params in body correctly', () => { const params = { url: 'https://example.{{param1}}/', method: 'GET', body: 'hello {{h}} {{b}} {{h1h1h1h1h1h1h1}} {{h2}} {{a}} {{h1h1h1h1h1h1h1}} {{h}} {{a}} {{h2}} {{a}} {{b}} world', geoLocation: 'US', responseMatches: [{ type: 'regex', value: '<title.*?(?<domain>{{param2}} Domain)<\\/title>', }], responseRedactions: [{ xPath: './html/head/{{param3}}', }, { xPath: '/html/body/div/p[1]/text()' }], paramValues: { param1: 'com', param2: 'Example', param3: 'title', what: 'illustrative', a: '{{b}}', b: 'aaaaa' }, headers: { 'user-agent': 'Mozilla/5.0', } }; const secretParams = { cookieStr: '<cookie-str>', paramValues: { h: 'crazy', h1h1h1h1h1h1h1: 'crazy1', h2: 'crazy2', }, authorisationHeader: 'abc' }; const req = createRequest(secretParams, params, utils_2.logger); const reqText = (0, utils_2.uint8ArrayToStr)(req.data); expect(reqText).toContain('hello crazy aaaaa crazy1 crazy2 {{b}} crazy1 crazy {{b}} crazy2 {{b}} aaaaa world'); expect(req.redactions.length).toEqual(7); expect(getRedaction(0)).toEqual('Cookie: <cookie-str>\r\nAuthorization: abc'); expect(getRedaction(1)).toEqual('crazy'); expect(getRedaction(2)).toEqual('crazy1'); expect(getRedaction(3)).toEqual('crazy2'); expect(getRedaction(4)).toEqual('crazy1'); expect(getRedaction(5)).toEqual('crazy'); expect(getRedaction(6)).toEqual('crazy2'); function getRedaction(index) { return (0, utils_2.uint8ArrayToStr)(req.data.slice(req.redactions[index].fromIndex, req.redactions[index].toIndex)); } }); it('should replace params in body correctly case 2', () => { const params = { 'body': '{"includeGroups":{{REQ_DAT}},"includeLogins":{{REQ_SECRET}},"includeVerificationStatus":false}', 'geoLocation': '', 'method': 'POST', 'paramValues': { 'REQ_DAT': 'false', 'username': 'testyreclaim' }, 'responseMatches': [ { 'type': 'contains', 'value': '"userName":"{{username}}"' } ], 'responseRedactions': [ { 'jsonPath': '$.userName', 'regex': '"userName":"(.*)"', 'xPath': '' } ], 'url': 'https://www.kaggle.com' }; const secretParams = { 'paramValues': { 'REQ_SECRET': 'false' }, authorisationHeader: 'abc' }; const req = createRequest(secretParams, params, utils_2.logger); const reqText = (0, utils_2.uint8ArrayToStr)(req.data); expect(reqText).toContain('{\"includeGroups\":false,\"includeLogins\":false,\"includeVerificationStatus\":false}'); expect(req.redactions.length).toEqual(2); expect(getRedaction(0)).toEqual('Authorization: abc'); expect(getRedaction(1)).toEqual('false'); function getRedaction(index) { return (0, utils_2.uint8ArrayToStr)(req.data.slice(req.redactions[index].fromIndex, req.redactions[index].toIndex)); } }); describe('OPRF', () => { it('should handle OPRF replacements', async () => { const params = { url: 'https://example.com/', method: 'GET', responseMatches: [ { type: 'regex', value: '<title>(?<domain>.+)<\\/title>', } ], responseRedactions: [ { regex: '<title>(?<domain>.+)<\\/title>', hash: 'oprf' } ], }; const res = Buffer.from('SFRUUC8xLjEgMjAwIE9LDQpBY2NlcHQtUmFuZ2VzOiBieXRlcw0KQWdlOiAzNzIxNDcNCkNhY2hlLUNvbnRyb2w6IG1heC1hZ2U9NjA0ODAwDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD1VVEYtOA0KRGF0ZTogVGh1LCAyMSBOb3YgMjAyNCAwNTozOTo0NiBHTVQNCkV0YWc6ICIzMTQ3NTI2OTQ3Ig0KRXhwaXJlczogVGh1LCAyOCBOb3YgMjAyNCAwNTozOTo0NiBHTVQNCkxhc3QtTW9kaWZpZWQ6IFRodSwgMTcgT2N0IDIwMTkgMDc6MTg6MjYgR01UDQpTZXJ2ZXI6IEVDQWNjIChsYWMvNTVCNSkNClZhcnk6IEFjY2VwdC1FbmNvZGluZw0KWC1DYWNoZTogSElUDQpDb250ZW50LUxlbmd0aDogMTI1Ng0KQ29ubmVjdGlvbjogY2xvc2UNCg0KPCFkb2N0eXBlIGh0bWw+CjxodG1sPgo8aGVhZD4KICAgIDx0aXRsZT5FeGFtcGxlIERvbWFpbjwvdGl0bGU+CgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiIC8+CiAgICA8bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LXR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD11dGYtOCIgLz4KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MSIgLz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CiAgICBib2R5IHsKICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjBmMGYyOwogICAgICAgIG1hcmdpbjogMDsKICAgICAgICBwYWRkaW5nOiAwOwogICAgICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBzeXN0ZW0tdWksIEJsaW5rTWFjU3lzdGVtRm9udCwgIlNlZ29lIFVJIiwgIk9wZW4gU2FucyIsICJIZWx2ZXRpY2EgTmV1ZSIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7CiAgICAgICAgCiAgICB9CiAgICBkaXYgewogICAgICAgIHdpZHRoOiA2MDBweDsKICAgICAgICBtYXJnaW46IDVlbSBhdXRvOwogICAgICAgIHBhZGRpbmc6IDJlbTsKICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmRmZGZmOwogICAgICAgIGJvcmRlci1yYWRpdXM6IDAuNWVtOwogICAgICAgIGJveC1zaGFkb3c6IDJweCAzcHggN3B4IDJweCByZ2JhKDAsMCwwLDAuMDIpOwogICAgfQogICAgYTpsaW5rLCBhOnZpc2l0ZWQgewogICAgICAgIGNvbG9yOiAjMzg0ODhmOwogICAgICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsKICAgIH0KICAgIEBtZWRpYSAobWF4LXdpZHRoOiA3MDBweCkgewogICAgICAgIGRpdiB7CiAgICAgICAgICAgIG1hcmdpbjogMCBhdXRvOwogICAgICAgICAgICB3aWR0aDogYXV0bzsKICAgICAgICB9CiAgICB9CiAgICA8L3N0eWxlPiAgICAKPC9oZWFkPgoKPGJvZHk+CjxkaXY+CiAgICA8aDE+RXhhbXBsZSBEb21haW48L2gxPgogICAgPHA+VGhpcyBkb21haW4gaXMgZm9yIHVzZSBpbiBpbGx1c3RyYXRpdmUgZXhhbXBsZXMgaW4gZG9jdW1lbnRzLiBZb3UgbWF5IHVzZSB0aGlzCiAgICBkb21haW4gaW4gbGl0ZXJhdHVyZSB3aXRob3V0IHByaW9yIGNvb3JkaW5hdGlvbiBvciBhc2tpbmcgZm9yIHBlcm1pc3Npb24uPC9wPgogICAgPHA+PGEgaHJlZj0iaHR0cHM6Ly93d3cuaWFuYS5vcmcvZG9tYWlucy9leGFtcGxlIj5Nb3JlIGluZm9ybWF0aW9uLi4uPC9hPjwvcD4KPC9kaXY+CjwvYm9keT4KPC9odG1sPgo=', 'base64'); const redactedStr = await getRedactedStr(res, params); // the transcript contained "Example Domain" in the title // which should be replaced with the hash expect(redactedStr).toContain('<title>AAAAAAAAAAAAAA</title>'); }); it('should handle OPRF replacements in a chunked res', async () => { const params = { url: 'https://example.com/', method: 'GET', responseMatches: [ { type: 'regex', value: '\"name\":\"(?<name>.+?)\"', }