#632: M1249352(remaining) M1358297(backbugs) M1369317(pp1,3,4) M1426996

This commit is contained in:
Cameron Kaiser 2020-11-22 20:57:19 -08:00
parent c1a28532cf
commit 1eab6170b1
5 changed files with 186 additions and 38 deletions

View File

@ -192,27 +192,23 @@ NS_IMETHODIMP
nsSimpleURI::SetSpec(const nsACString &aSpec)
{
NS_ENSURE_STATE(mMutable);
// filter out unexpected chars "\r\n\t" if necessary
nsAutoCString filteredSpec;
net_FilterURIString(aSpec, filteredSpec);
const char* specPtr = filteredSpec.get();
int32_t specLen = filteredSpec.Length();
// nsSimpleURI currently restricts the charset to US-ASCII
nsAutoCString spec;
NS_EscapeURL(specPtr, specLen, esc_OnlyNonASCII|esc_AlwaysCopy, spec);
int32_t colonPos = spec.FindChar(':');
if (colonPos < 0 || !net_IsValidScheme(spec.get(), colonPos))
return NS_ERROR_MALFORMED_URI;
mScheme.Truncate();
mozilla::DebugOnly<int32_t> n = spec.Left(mScheme, colonPos);
NS_ASSERTION(n == colonPos, "Left failed");
nsresult rv = net_ExtractURLScheme(aSpec, mScheme);
if (NS_FAILED(rv)) {
return rv;
}
ToLowerCase(mScheme);
nsAutoCString spec;
rv = net_FilterAndEscapeURI(aSpec, esc_OnlyNonASCII, spec);
if (NS_FAILED(rv)) {
return rv;
}
int32_t colonPos = spec.FindChar(':');
MOZ_ASSERT(colonPos != kNotFound, "A colon should be in this string");
// This sets both mPath and mRef.
// If changed, see bug 1369317, especially part 2 -- Cameron
return SetPath(Substring(spec, colonPos + 1));
}
@ -383,7 +379,13 @@ nsSimpleURI::SetRef(const nsACString &aRef)
{
NS_ENSURE_STATE(mMutable);
if (aRef.IsEmpty()) {
nsAutoCString ref;
nsresult rv = NS_EscapeURL(aRef, esc_OnlyNonASCII, ref, fallible);
if (NS_FAILED(rv)) {
return rv;
}
if (ref.IsEmpty()) {
// Empty string means to remove ref completely.
mIsRefValid = false;
mRef.Truncate(); // invariant: mRef should be empty when it's not valid
@ -513,12 +515,11 @@ nsSimpleURI::Resolve(const nsACString &relativePath, nsACString &result)
}
NS_IMETHODIMP
nsSimpleURI::GetAsciiSpec(nsACString &result)
nsSimpleURI::GetAsciiSpec(nsACString &aResult)
{
nsAutoCString buf;
nsresult rv = GetSpec(buf);
nsresult rv = GetSpec(aResult);
if (NS_FAILED(rv)) return rv;
NS_EscapeURL(buf, esc_OnlyNonASCII|esc_AlwaysCopy, result);
MOZ_ASSERT(IsASCII(aResult), "The spec should be ASCII");
return NS_OK;
}

View File

@ -9,6 +9,7 @@
#include <algorithm>
#include <iterator>
#include "nsASCIIMask.h"
#include "nsURLHelper.h"
#include "nsIFile.h"
#include "nsIURLParser.h"
@ -18,6 +19,7 @@
#include "mozilla/Preferences.h"
#include "prnetdb.h"
#include "mozilla/Tokenizer.h"
#include "nsEscape.h"
#include "mozilla-config.h"
#include "plvmx.h"
@ -543,7 +545,7 @@ net_ExtractURLScheme(const nsACString &inURI,
}
p.Claim(scheme);
scheme.StripChars("\r\n\t");
scheme.StripTaggedASCII(ASCIIMask::MaskCRLFTab());
return NS_OK;
#endif
}
@ -615,8 +617,6 @@ net_IsAbsoluteURL(const nsACString& uri)
void
net_FilterURIString(const nsACString& input, nsACString& result)
{
const char kCharsToStrip[] = "\r\n\t";
result.Truncate();
auto start = input.BeginReading();
auto end = input.EndReading();
@ -630,9 +630,14 @@ net_FilterURIString(const nsACString& input, nsACString& result)
charFilter).base();
// Check if chars need to be stripped.
auto itr = std::find_first_of(
newStart, newEnd, std::begin(kCharsToStrip), std::end(kCharsToStrip));
const bool needsStrip = itr != newEnd;
bool needsStrip = false;
const ASCIIMaskArray& mask = ASCIIMask::MaskCRLFTab();
for (auto itr = start; itr != end; ++itr) {
if (ASCIIMask::IsMasked(mask, *itr)) {
needsStrip = true;
break;
}
}
// Just use the passed in string rather than creating new copies if no
// changes are necessary.
@ -643,10 +648,31 @@ net_FilterURIString(const nsACString& input, nsACString& result)
result.Assign(Substring(newStart, newEnd));
if (needsStrip) {
result.StripChars(kCharsToStrip);
result.StripTaggedASCII(mask);
}
}
nsresult
net_FilterAndEscapeURI(const nsACString& aInput, uint32_t aFlags, nsACString& aResult)
{
aResult.Truncate();
auto start = aInput.BeginReading();
auto end = aInput.EndReading();
// Trim off leading and trailing invalid chars.
auto charFilter = [](char c) { return static_cast<uint8_t>(c) > 0x20; };
auto newStart = std::find_if(start, end, charFilter);
auto newEnd = std::find_if(
std::reverse_iterator<decltype(end)>(end),
std::reverse_iterator<decltype(newStart)>(newStart),
charFilter).base();
const ASCIIMaskArray& mask = ASCIIMask::MaskCRLFTab();
return NS_EscapeAndFilterURL(Substring(newStart, newEnd), aFlags,
&mask, aResult, fallible);
}
#if defined(XP_WIN)
bool
net_NormalizeFileURL(const nsACString &aURL, nsCString &aResultBuf)

View File

@ -115,6 +115,18 @@ inline bool net_IsValidScheme(const nsAFlatCString &scheme)
*/
void net_FilterURIString(const nsACString& input, nsACString& result);
/**
* This function performs character stripping just like net_FilterURIString,
* with the added benefit of also performing percent escaping of dissallowed
* characters, all in one pass. Saving one pass is very important when operating
* on really large strings.
*
* @param aInput the URL spec we want to filter
* @param aFlags the flags which control which characters we escape
* @param aResult the out param to write to if filtering happens
*/
nsresult net_FilterAndEscapeURI(const nsACString& aInput, uint32_t aFlags, nsACString& aResult);
#if defined(XP_WIN)
/**
* On Win32 and OS/2 system's a back-slash in a file:// URL is equivalent to a

View File

@ -11,6 +11,7 @@
#include "nsTArray.h"
#include "nsCRT.h"
#include "plstr.h"
#include "nsASCIIMask.h"
#include "mozilla-config.h"
#include "plvmx.h"
@ -381,10 +382,23 @@ static uint16_t dontNeedEscape(uint16_t aChar, uint32_t aFlags)
//----------------------------------------------------------------------------------------
/**
* Templated helper for URL escaping a portion of a string.
*
* @param aPart The pointer to the beginning of the portion of the string to
* escape.
* @param aPartLen The length of the string to escape.
* @param aFlags Flags used to configure escaping. @see EscapeMask
* @param aResult String that has the URL escaped portion appended to. Only
* altered if the string is URL escaped or |esc_AlwaysCopy| is specified.
* @param aDidAppend Indicates whether or not data was appended to |aResult|.
* @return NS_ERROR_INVALID_ARG, NS_ERROR_OUT_OF_MEMORY on failure.
*/
template<class T>
static bool
static nsresult
T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
uint32_t aFlags, T& aResult)
uint32_t aFlags, const ASCIIMaskArray* aFilterMask,
T& aResult, bool& aDidAppend)
{
typedef nsCharTraits<typename T::char_type> traits;
typedef typename traits::unsigned_char_type unsigned_char_type;
@ -393,7 +407,7 @@ T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
if (!aPart) {
NS_NOTREACHED("null pointer");
return false;
return NS_ERROR_INVALID_ARG;
}
bool forced = !!(aFlags & esc_Forced);
@ -411,6 +425,19 @@ T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
for (size_t i = 0; i < aPartLen; ++i) {
unsigned_char_type c = *src++;
// If there is a filter, we wish to skip any characters which match it.
// This is needed so we don't perform an extra pass just to extract the
// filtered characters.
if (aFilterMask && ASCIIMask::IsMasked(*aFilterMask, c)) {
if (!writing) {
if (!aResult.Append(aPart, i, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
writing = true;
}
continue;
}
// if the char has not to be escaped or whatever follows % is
// a valid escaped string, just copy the char.
//
@ -437,7 +464,9 @@ T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
}
} else { /* do the escape magic */
if (!writing) {
aResult.Append(aPart, i);
if (!aResult.Append(aPart, i, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
writing = true;
}
uint32_t len = ::AppendPercentHex(tempBuffer + tempBufferPos, c);
@ -448,16 +477,21 @@ T_EscapeURL(const typename T::char_type* aPart, size_t aPartLen,
// Flush the temp buffer if it doesnt't have room for another encoded char.
if (tempBufferPos >= mozilla::ArrayLength(tempBuffer) - ENCODE_MAX_LEN) {
NS_ASSERTION(writing, "should be writing");
aResult.Append(tempBuffer, tempBufferPos);
if (!aResult.Append(tempBuffer, tempBufferPos, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
tempBufferPos = 0;
}
previousIsNonASCII = (c > 0x7f);
}
if (writing) {
aResult.Append(tempBuffer, tempBufferPos);
if (!aResult.Append(tempBuffer, tempBufferPos, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
return writing;
aDidAppend = writing;
return NS_OK;
}
bool
@ -467,13 +501,66 @@ NS_EscapeURL(const char* aPart, int32_t aPartLen, uint32_t aFlags,
if (aPartLen < 0) {
aPartLen = strlen(aPart);
}
return T_EscapeURL(aPart, aPartLen, aFlags, aResult);
bool result = false;
nsresult rv = T_EscapeURL(aPart, aPartLen, aFlags, nullptr, aResult, result);
if (NS_FAILED(rv)) {
::NS_ABORT_OOM(aResult.Length() * sizeof(nsACString::char_type));
}
return result;
}
nsresult
NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult,
const mozilla::fallible_t&)
{
bool appended = false;
nsresult rv = T_EscapeURL(aStr.Data(), aStr.Length(), aFlags, nullptr, aResult, appended);
if (NS_FAILED(rv)) {
aResult.Truncate();
return rv;
}
if (!appended) {
if (!aResult.Assign(aStr, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
return rv;
}
nsresult
NS_EscapeAndFilterURL(const nsACString& aStr, uint32_t aFlags,
const ASCIIMaskArray* aFilterMask,
nsACString& aResult, const mozilla::fallible_t&)
{
bool appended = false;
nsresult rv = T_EscapeURL(aStr.Data(), aStr.Length(), aFlags, aFilterMask, aResult, appended);
if (NS_FAILED(rv)) {
aResult.Truncate();
return rv;
}
if (!appended) {
aResult = aStr;
}
return rv;
}
const nsSubstring&
NS_EscapeURL(const nsSubstring& aStr, uint32_t aFlags, nsSubstring& aResult)
{
if (T_EscapeURL<nsSubstring>(aStr.Data(), aStr.Length(), aFlags, aResult)) {
bool result = false;
nsresult rv = T_EscapeURL<nsSubstring>(aStr.Data(), aStr.Length(), aFlags, nullptr, aResult, result);
if (NS_FAILED(rv)) {
::NS_ABORT_OOM(aResult.Length() * sizeof(nsSubstring::char_type));
}
if (result) {
return aResult;
}
return aStr;

View File

@ -155,6 +155,28 @@ NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult)
}
return aStr;
}
/**
* Fallible version of NS_EscapeURL. On success aResult will point to either
* the original string or an escaped copy.
*/
nsresult
NS_EscapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult,
const mozilla::fallible_t&);
// Forward declaration for nsASCIIMask.h
typedef std::array<bool, 128> ASCIIMaskArray;
/**
* The same as NS_EscapeURL, except it also filters out characters that match
* aFilterMask.
*/
nsresult
NS_EscapeAndFilterURL(const nsACString& aStr, uint32_t aFlags,
const ASCIIMaskArray* aFilterMask,
nsACString& aResult, const mozilla::fallible_t&);
inline const nsCSubstring&
NS_UnescapeURL(const nsCSubstring& aStr, uint32_t aFlags, nsCSubstring& aResult)
{