diff --git a/package.json b/package.json index c33dc87..12399bc 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "type": "module", "scripts": { - "build": "deno build.ts --tagName 1.67.1b3 > ubo.js", + "build": "deno build.ts --tagName 1.67.1b5 > ubo.js", "test": "node --test" }, "author": { diff --git a/ubo.js b/ubo.js index e59c372..061f858 100644 --- a/ubo.js +++ b/ubo.js @@ -19218,6 +19218,45 @@ function proxyApplyFn( } context[prop] = new Proxy(fn, proxyDetails); } +function parsePropertiesToMatchFn(propsToMatch, implicit = '') { + const safe = safeSelf(); + const needles = new Map(); + if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } + const options = { canNegate: true }; + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + let [ prop, pattern ] = safe.String_split.call(needle, ':'); + if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } + if ( pattern !== undefined ) { + needles.set(prop, safe.initPattern(pattern, options)); + } else if ( implicit !== '' ) { + needles.set(implicit, safe.initPattern(prop, options)); + } + } + return needles; +} +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -19480,20 +19519,7 @@ function preventFetchFn( responseType ); const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); - const needles = []; - for ( const condition of safe.String_split.call(propsToMatch, /\s+/) ) { - if ( condition === '' ) { continue; } - const pos = condition.indexOf(':'); - let key, value; - if ( pos !== -1 ) { - key = condition.slice(0, pos); - value = condition.slice(pos + 1); - } else { - key = 'url'; - value = condition; - } - needles.push({ key, pattern: safe.initPattern(value, { canNegate: true }) }); - } + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); const validResponseProps = { ok: [ false, true ], statusText: [ '', 'Not Found' ], @@ -19523,41 +19549,38 @@ function preventFetchFn( } proxyApplyFn('fetch', function fetch(context) { const { callArgs } = context; - const details = callArgs[0] instanceof self.Request - ? callArgs[0] - : Object.assign({ url: callArgs[0] }, callArgs[1]); - let proceed = true; - try { - const props = new Map(); - for ( const prop in details ) { - let v = details[prop]; - if ( typeof v !== 'string' ) { - try { v = safe.JSON_stringify(v); } - catch { } - } - if ( typeof v !== 'string' ) { continue; } - props.set(prop, v); - } - if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { - const out = Array.from(props).map(a => `${a[0]}:${a[1]}`); - safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); - } - if ( propsToMatch === '' && responseBody === '' ) { - return context.reflect(); - } - proceed = needles.length === 0; - for ( const { key, pattern } of needles ) { - if ( - pattern.expect && props.has(key) === false || - safe.testPattern(pattern, props.get(key)) === false - ) { - proceed = true; - break; + const details = (( ) => { + const fetchProps = (src, out) => { + if ( typeof src !== 'object' || src === null ) { return; } + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'signal', + ]; + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; } + }; + const out = {}; + if ( callArgs[0] instanceof self.Request ) { + out.url = `${callArgs[0].url}`; + fetchProps(callArgs[0], out); + } else { + out.url = `${callArgs[0]}`; } - } catch { + fetchProps(callArgs[1], out); + return out; + })(); + if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { + const out = Array.from(details).map(a => `${a[0]}:${a[1]}`); + safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); + } + if ( propsToMatch === '' && responseBody === '' ) { + return context.reflect(); } - if ( proceed ) { + const matched = matchObjectPropertiesFn(propNeedles, details); + if ( matched === undefined ) { return context.reflect(); } return Promise.resolve(generateContentFn(trusted, responseBody)).then(text => { @@ -19678,6 +19701,45 @@ function proxyApplyFn( } context[prop] = new Proxy(fn, proxyDetails); } +function parsePropertiesToMatchFn(propsToMatch, implicit = '') { + const safe = safeSelf(); + const needles = new Map(); + if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } + const options = { canNegate: true }; + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + let [ prop, pattern ] = safe.String_split.call(needle, ':'); + if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } + if ( pattern !== undefined ) { + needles.set(prop, safe.initPattern(pattern, options)); + } else if ( implicit !== '' ) { + needles.set(implicit, safe.initPattern(prop, options)); + } + } + return needles; +} +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -19940,20 +20002,7 @@ function preventFetchFn( responseType ); const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); - const needles = []; - for ( const condition of safe.String_split.call(propsToMatch, /\s+/) ) { - if ( condition === '' ) { continue; } - const pos = condition.indexOf(':'); - let key, value; - if ( pos !== -1 ) { - key = condition.slice(0, pos); - value = condition.slice(pos + 1); - } else { - key = 'url'; - value = condition; - } - needles.push({ key, pattern: safe.initPattern(value, { canNegate: true }) }); - } + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); const validResponseProps = { ok: [ false, true ], statusText: [ '', 'Not Found' ], @@ -19983,41 +20032,38 @@ function preventFetchFn( } proxyApplyFn('fetch', function fetch(context) { const { callArgs } = context; - const details = callArgs[0] instanceof self.Request - ? callArgs[0] - : Object.assign({ url: callArgs[0] }, callArgs[1]); - let proceed = true; - try { - const props = new Map(); - for ( const prop in details ) { - let v = details[prop]; - if ( typeof v !== 'string' ) { - try { v = safe.JSON_stringify(v); } - catch { } - } - if ( typeof v !== 'string' ) { continue; } - props.set(prop, v); - } - if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { - const out = Array.from(props).map(a => `${a[0]}:${a[1]}`); - safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); - } - if ( propsToMatch === '' && responseBody === '' ) { - return context.reflect(); - } - proceed = needles.length === 0; - for ( const { key, pattern } of needles ) { - if ( - pattern.expect && props.has(key) === false || - safe.testPattern(pattern, props.get(key)) === false - ) { - proceed = true; - break; + const details = (( ) => { + const fetchProps = (src, out) => { + if ( typeof src !== 'object' || src === null ) { return; } + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'signal', + ]; + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; } + }; + const out = {}; + if ( callArgs[0] instanceof self.Request ) { + out.url = `${callArgs[0].url}`; + fetchProps(callArgs[0], out); + } else { + out.url = `${callArgs[0]}`; } - } catch { + fetchProps(callArgs[1], out); + return out; + })(); + if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { + const out = Array.from(details).map(a => `${a[0]}:${a[1]}`); + safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); + } + if ( propsToMatch === '' && responseBody === '' ) { + return context.reflect(); } - if ( proceed ) { + const matched = matchObjectPropertiesFn(propNeedles, details); + if ( matched === undefined ) { return context.reflect(); } return Promise.resolve(generateContentFn(trusted, responseBody)).then(text => { @@ -22587,6 +22633,7 @@ function trustedReplaceArgument( const parsed = parseReplaceFn(argraw.slice(5)); if ( parsed === undefined ) { return; } replacer = arg => `${arg}`.replace(replacer.re, replacer.replacement); + Object.assign(replacer, parsed); } else if ( argraw.startsWith('add:') ) { const delta = parseFloat(argraw.slice(4)); if ( isNaN(delta) ) { return; }