def response(self, flow: http.HTTPFlow): response = flow.response if CONTENT_TYPE in response.headers: if any( map(lambda t: t in response.headers[CONTENT_TYPE], RELEVANT_CONTENT_TYPES)): # Response is a web page; proceed. insertedScripts: List[str] = [] soup = BeautifulSoup(response.content, HTML_PARSER, from_encoding=inferEncoding(response)) requestURL = flow.request.pretty_url # should work in transparent mode too, unless the Host header is spoofed isApplicable: Callable[[Userscript], bool] = userscript.applicableChecker( requestURL) for script in self.userscripts: if isApplicable(script): useInline = ctx.options.inline or script.downloadURL is None if useInline and len(script.unsafeSequences) > 0: logError(unsafeSequencesMessage(script)) continue logInfo( f"""Injecting {script.name}{"" if script.version is None else " " + VERSION_PREFIX + script.version} into {requestURL} ({"inline" if useInline else "linked"}) ...""" ) result = inject( script, soup, Options( inline=ctx.options.inline, verbose=ctx.options.verbose, )) if type(result) is BeautifulSoup: soup = result insertedScripts.append(script.name + ( "" if script.version is None else " " + stringifyVersion(script.version))) else: logError( "Injection failed due to the following error:") logError(str(result)) index_DTD: Optional[int] = indexOfDTD(soup) # Insert information comment: if ctx.options.verbose: soup.insert( 0 if index_DTD is None else 1 + index_DTD, Comment(INFO_COMMENT_PREFIX + ("No matching userscripts for this URL." if insertedScripts == [] else "These scripts were inserted:\n" + bulletList(insertedScripts)) + "\n")) # Prevent BS/html.parser from emitting `<!DOCTYPE doctype html>` or similar if "DOCTYPE" is not all uppercase in source HTML: if index_DTD is not None and REGEX_DOCTYPE.match( soup.contents[index_DTD]): # There is a DTD and it is invalid, so replace it. soup.contents[index_DTD] = Doctype( re.sub(REGEX_DOCTYPE, "", soup.contents[index_DTD])) # Serialize and encode: response.content = str(soup).encode( fromOptional(soup.original_encoding, CHARSET_DEFAULT), "replace")
def response(self, flow: http.HTTPFlow): response = flow.response if CONTENT_TYPE in response.headers: if any( map(lambda t: t in response.headers[CONTENT_TYPE], RELEVANT_CONTENT_TYPES)): # Response is a web page; proceed. insertedScripts: List[str] = [] soup = BeautifulSoup(response.content, HTML_PARSER, from_encoding=inferEncoding(response)) requestURL = flow.request.pretty_url # should work in transparent mode too, unless the Host header is spoofed if requestContainsQueryParam( option(T.option_query_param_to_disable), flow.request): logInfo( f"""Not injecting any userscripts into {requestURL} because it contains a `{option(T.option_query_param_to_disable)}` query parameter.""" ) return isApplicable: Callable[[Userscript], bool] = userscript.applicableChecker( requestURL) for script in self.userscripts: if isApplicable(script): useInline = option( T.option_inline) or script.downloadURL is None if useInline and len(script.unsafeSequences) > 0: logError(unsafeSequencesMessage(script)) continue logInfo( f"""Injecting {script.name}{"" if script.version is None else " " + VERSION_PREFIX + script.version} into {requestURL} ({"inline" if useInline else "linked"}) ...""" ) result = inject( script, soup, Options(inline=option(T.option_inline), )) if type(result) is BeautifulSoup: soup = result insertedScripts.append(script.name + ( "" if script.version is None else " " + T.stringifyVersion(script.version))) else: logError( "Injection failed due to the following error:") logError(str(result)) index_DTD: Optional[int] = indexOfDTD(soup) # Insert information comment: if option(T.option_list_injected): soup.insert( 0 if index_DTD is None else 1 + index_DTD, Comment(HTML_INFO_COMMENT_PREFIX + ("No matching userscripts for this URL." if insertedScripts == [] else "These scripts were inserted:\n" + bulletList(insertedScripts)) + "\n")) # Serialize and encode: response.content = str(soup).encode( fromOptional(soup.original_encoding, CHARSET_DEFAULT), "replace")
def insertLateIn(soup: BeautifulSoup, tag: Tag): fromOptional(soup.body, soup).append(tag)