mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
116abd772d
- Bug 1263951 - Avoid trying to initialize new GMP instances once the browser enters shutdown. r=cpearce (3ca153c7f4) - Bug 1268714 - Check for failure result or a failed nsresult from SendLoadGMP. r=mccr8 (928546a72a) - remove redundant decl (0c7c81e384) - Bug 1161339 - Add gtest calling rust code. r=cajbir (9c0a4982d7) - Bug 1269249: [MSE] P1. Clamp range to media source duration when media source is ended. r=jwwang (dfc42686b5) - Bug 1269178: P4. Add mochitest. r=gerald (37fe5f9232) - Bug 1269249: [MSE] P2. Add mochitest verifying behavior. r=jwwang (f50fb0d648) - Bug 1245052 - various media b2g build errors r=jya (1fc0f3b8f1) - Bug 1239598 - Fix potential deadlock and race condition r=bechen (dfebc1b9c5) - Bug 1185931 - Add assert(mDecoderStateMachine). r=jwwang (16a79dd863) - bits of Bug 1160695 (a595535a04) - Bug 1205209 - Check whether mStreamSource is null in MediaOmxReader. r=bechen (3e380b282d) - Bug 1210286 - Fall back to converting SourceSurfaces (RGB) to NV12 in OMXCodecWrapper. r=jolin (b9e26a43ee) - Bug 1090015 - Suppress multichar warnings in the OMX code. r=kinetik (13cea78721) - missing bit of Bug 1137151: Remove ref-counting from |OMXVideoEncoder| (0abf3cccf3) - Bug 1239610 - Remove GonkNativeWindowClient usage from OmxDecoder r=jolin (a1ccc2a40e) - missing bit of 1198576 (c3284a3002) - Bug 1267637: P1. Consider invalid an AudioData with more than 8 audio channels. r=gerald (9bacf3fa8d) - Bug 1267637: P2. Ignore outright audio track considered invalid. r=gerald (d34b468c87) - Bug 1267637: [opus] P3. Reject audio data with unsupported audio configuration. r=gerald (90be7f8e3a) - Bug 1267637: [vorbis] P4. Reject audio data with unsupported audio configuration. r=gerald (2321df4669) - Bug 1267637: [AT] P6. Reject audio data with unsupported channel configuration. r=gerald (48756a764b) - Bug 1267637: [ffmpeg] P7. Reject audio data with unsupported channel configuration. r=gerald (bbf90018b5) - Bug 1267637: [gonk] P8. Reject audio data with unsupported channel configuration. r=gerald (44043594f0) - Bug 1199809 - Remove all references to unused task queue. r=jya (829bb54ce7) - Bug 1199809 - Don't schedule decoder I/O task when there will be more input. r=bwu (284c8b28d4) - Bug 1215441 - Skip flush before Init() is completed. r=sotaro (f59f4ae450) - Bug 1207214 - Assert decoder attaches EOS flag to final output buffer. r=sotaro (da0fc1f41b) - Bug 1217220 - use output timestamp to decide which item needs to be removed from waiting list. r=jya (b056c21000) - Bug 1222919 - Make ProcessFlush() virtual. r=jya (bea87e8e8a) - Bug 1259366 - Flush after eos of android::MediaCodec r=jolin (c8e1e038ad) - Bug 1199809 - Reset last decoded frame time on looper thread to avoid race condition. r=jya (1d56c95439) - cleanup (83701e29ea) - Bug 1267637: [wmf] P9. Reject audio data with unsupported channel configuration. r=gerald (c1f32cf152) - Bug 1264925: Force D3D9 when attempting to decode VP8 or VP9. r=mattwoodrow (2a7d853fe6) - Bug 1162899 - Use sync message for in-process mozHasPendingMessage. r=fabrice (d28430d0b2) - Bug 1235484 - Part 1: Refine radio state check in MmsService. r=bevistseng (5f00e3ec79) - remove android, fix some missing tests (01b371eb39) - Bug 1259148 - Notify content when the notification permission pop-up is dismissed by the user. r=past,wchen (ca2dfbf92f) - Bug 1267357 Cycle collect NotificationPermissionRequest::mCallback. r=mccr8 (6d0086af08) - Bug 1265828 - Remove persistent notifications from storage. r=wchen (230d6f8458) - Bug 1254816 - Use IgnoredErrorResult instead of ErrorResult for rv in nsDOMOfflineResourceList::Length(). r=bz (581f5c69db) - Bug 1140478: Free the string returned by PrintJSStack(), in android shutdown logging function. r=jorendorff (ebf6ef80d1) - Bug 1191137 - fix Mulet and responsive design mode to send key events properly r=ochameau,jryans (d47db5bf2b) - Bug 1267096 - Return early if we have no global when creating a Promise. r=smaug (d4075f38fe) - Bug 1146418 - Promise API entry points should use NS_ASSERT_OWNINGTHREAD, r=baku (a43b49c451) - Bug 1262069: Wrap promise resolution values before storing. r=bz (66ebb63ce1) - Bug 1246073 - Fix unique constraint errors in the H2 backend when resubscribing. r=dragana (43c67dc6bc) - Bug 1266433 - Clean up nsIPushNotifier static casts. r=dragana (eef5497c75) - Bug 1266433 - Update the comments in the Push XPIDL interfaces. r=me (35cd32c385) - Bug 1266433 - Indicate push subscriptions created by privileged code. r=dragana (824381dd69) - Bug 1242436 - default value of ok = true in order to check the return of SendPush and SendPushSubscriptionChange. r=kitcambridge (da55effde0) - Bug 1267889 - Always steal the error result in PushMessage::Json. r=dragana (a66bf171b6) - Bug 1243778 - PushRecord::getLastVisit cannot rely on the Places url index anymore. r=kitcambridge (07c3bdc4db) - Bug 1260499 - Handle incoming messages before push service is initialized. r=nalexander,jchen (d60ccda56b) - Bug 1265915 - Remove adaptive pings from the Push WebSocket backend. r=dragana (c8de7d5dd3) - Bug 1262559: Fix misspelled comment in dom/push/PushServiceWebSocket.jsm; r=jdm (a9d869773d) - Bug 1265914 - Remove Push UDP wake-up. r=dragana (128cf912bb) - Bug 1261634 - Update whitespace skipping for meta csp. r=dveditz (c6abb5c502) - Bug 1227813 - CSP: Ignore unsafe-inline within style-src if hash or nonce specified. r=kmckinley (ab1db6e779) - Bug 1192840 - Fix CSP report content-type. r=ckerschb (81c55b28f0) - Bug 1262635 - Don't strip URIs of ftp: when sending reports. r=dveditz (77e9aacac1) - Bug 1216365 - nsMixedContentBlocker should use innerMostURI for aContentLocation. r=tanvi (9134c28268) - Bug 1260153 - remove unreachable code in nsMixedContentBlocker. if/else blocks above all return. r=ckerschb (da297bf7ff) - Bug 1202374 - "Can't open apps any more". r=lissyx+mozillians (ed090dbfce) - Bug 1216498 - Bump SettinsDB version in order to enable pin the web. r=mhenretty (e2d0e9986f) - Bug 1226906 - Bump Settings BD version. r=gwagner (e063a2d482) - Bug 1119727 - Make Settings Soft Lockup threshold configurable. r=gwagner (1f5d9d3a50) - var-let (48c1155a0e) - bit of Backed out 2 changesets (bug 1202902) (828b6981a8) - Bug 1228673 - Clean-up 'Then' before ~MediaTimer. r=jya (e51663a94b) - align tests (0fbfa6f14b) - Bug 1152236: OMX codec should use AnnexB as input format. r=jya (fc7ed581ac) - Bug 1153895 - Support audio AMR-WB for Gonk in MP4Reader. r=jya (d9a530979e) - Bug 1174623 - Support mp3 in Gonk PDM. r=sotaro (1f6efd4397) - Bug 1174166 - Support H.263 in Gonk PDM. r=sotaro (5634e66ce3) - Bug 1251155 - Remove GLContext from VideoDataDecoder r=snorp (814629fcd9) - Bug 1262456 - [2.1] Replace queue adapter with deque. r=snorp (80587b789d) - Bug 1262456 - [1.1] Prevent interruption of the decoder shutdown procedure and early shutdown return. r=snorp (98b97f0b48) - Bug 1248792 - [1.2] Replace MediaRawData raw pointers with RefPtr. r=snorp (c1c62c4dc6) - Bug 1226730 - [1.1] Provide sample rate instead of bit depth in audio format creation. r=snorp (86e50d757f) - Bug 1244292 - [1.2] Release decoder on init failure. r=snorp (48b18cf8dd) - Bug 1267637: [android] P5. Reject audio data with unsupported audio configuration. r=gerald (cf74f43c71) - Bug 1230784 - Don't copy SurfaceTexture contents when presenting video on Android r=esawin,jya (fbd0ffd1ea) - Bug 1230768. r=jesup (1619923f64) - Bug 1205164 - Make sure ShmemPool high water mark is logged in all allocators. r=jesup (1fa4710751) - Bug 1205164 - Fix ShmemPool detection of failed allocations. r=jesup (070327ef38) - Bug 1247933 - do not perform null check on aClient since we know for sure it's a valid pointert. r=sotaro (1ac12b4091) - Bug 1200903 - Fix MediaSystemResourceService::RemoveRequests() r=cpearce (13676b47c7) - Bug 1250083 - make sure value attributed to usPerDataChunk is floating point value. r=cpearce (af9c3bd65d) - Bug 1250497: Initalised Values used in WaveDemuxer.cpp. r=cpearce (36a24774ed) - Bug 1250293 - Fixed Coverity warning in WaveDemuxer.cpp. r=cpearce (f5a690fac4) - bug 1220042 make AlignedTArray base class inheritance private r=jwwang (3dd9efaa68) - Bug 877662 - Add AlignmentUtils.h r=padenot (82b7563dd7) - bug 930257 schedule Analyser inactive check when sending last null chunk r=padenot (61e9b5cf0f) - bug 1197028 add MOZ_IMPLICIT for AudioBlock constructor from base AudioChunk on CLOSED TREE (0e2a86c6b5) - bug 1203380 add custom AudioBlock copy constructor and make AudioChunk conversion constructor explicit r=padenot (56f2e37d5b) - bug 1205540 initialize mBufferFormat when constructing silent block r=padenot (078feb4af4) - bug 1203380 ClearDownstreamMark() before returning AsMutableChunk() r=padenot (56ea1f589f) - bug 1203380 tighten not-sharing assertion in ChannelFloatsForWrite() r=padenot (368e354a06) - bug 1203380 add custom assignment operator to AudioBlock r=padenot (31f9f914d1) - Bug 877662 - Align audio buffer allocations to 16 byte boundaries r=padenot (5f33e9dd0e) - Bug 1173016 - Bustage fix: mark BasicWaveformCache's ctor as `explicit`, on a CLOSED TREE. (bb28bb93fc) - Bug 1265397 - Add a length attribute to OfflineAudioContext. r=smaug (3c2cf2aee6) - bug 1255618 remove AudioContext from global window at unlink r=Ehsan (5332c8b42b) - bug 1222202 implement query interface to nsIMemoryReporter r=bz (e6662d2ba8) - Bug 1259831 - Remove the auto-suspend logic for AudioContext. r=karlt (3376fb3209) - Bug 1232326 - Uninitialised value use in AudioBufferInPlaceScale. r=dminor. (548cbbfa52) - Bug 1240054 - Only rebuild BandLimitedTables if more partials are required r=padenot (39bf05e142) - Bug 1216081 - OscillatorNodeEngine::mFinalFrequency is used uninitialised. r=padenot. (5989729ec6) - Bug 1265405 - Use a dictionary to specify how PeriodicWave should be normalized (or not); r=padenot (f43e3f17ba) - Bug 1267096 - Check the return value of Promise::Create in AudioContext::StartRendering. r=smaug (5efca6cfe8) - Bug 1267579 - Unexpected result when using OscillatorNode with custom wave shape; r=padenot (ed62d1b496) - Bug 1209904 - Optimize OscillatorNode when its frequency is not changin and it's using ::ComputeCustom. r=karlt (9a2246cc3f) - Bug 877662 - Use SSE2 versions of AudioNodeEngine functions r=padenot (1efa0b2cf3) - Bug 877662 - Add an SSE2 implementation of AudioNodeEngine.cpp functions. r=ehsan (ce5ff146e5) - Bug 877662 - Update SSE2 versions of AudioNodeEngine functions r=padenot (ded51f436e) - Bug 1266405 - AudioBufferSourceNode::CopyFromBuffer should not borrow unaligned buffers; r=padenot (a2880e5c97) - bug 1227411 add some initial logging of AudioNode API use r=padenot (6d0febaf34) - bug 1199561 delay offline buffer allocation until non-null input is received r=padenot (38d56f3e89) - Bug 1110344 - Replace float by double in AudioTimelineEvent ctor to prevent a rounding issue. r=padenot (c358371cfa) - Bug 1231124 - addded mCurve to constructor. r=smaug (572fed89d6) - Bug 1232646 - initialize 3 variables: mCurve, mTimeConstant, mDuration. r=cpearce (e36b3dbb71) - Bug 1069825 - Check if we compare two automation curves occuring at the same time during overlap checking. r=padenot (35624be622) - bug 1227411 add WEB_AUDIO_API_LOG r=padenot (89de67e91b) - bug 1189168 avoid main thread assertion accessing mNode in SizeOfIncludingThis() r=padenot (370df2ff5b) - Bug 1266047 - Fix crash in mozilla::AudioBufferAddWithScale_SSE r=padenot (388de2edf6) - Bug 1266772 - Unbreak FreeBSD build after bug 881587. r=dminor (e194878792) - Bug 1266112 - Remove unnecessary alignment checks from AudioNodeEngine.cpp; r=padenot (284ac98016) - Bug 881587 - Add SSE2 version of AudioNodeEngine.cpp routines added in bug 815643. r=tterribe (5532515d07) - Bug 881587 - Use SSE2 version of AudioNodeEngine.cpp routines added in bug 815643. r=padenot (82100493a4) - Bug 1105513 - Add a NEON version for AudioBlockPanStereoToStereo when aIsOnTheLeft is an array r=padenot (b102beb60d) - Bug 1203836 - Properly handle silent chunks in AudioNodeExternalInputStream. r=karlt (0597e5c122) - Bug 1265131, part 1 - update moz.build for Skia m51. r=jrmuizel (745537cf9b) - Bug 1265131, part 2 - update SkiaGLGlue for Skia m51. r=jrmuizel (71a3ffc91e) - Bug 1265131, part 3 - update Moz2d for Skia m51. r=jrmuizel (2129a455cb) - Bug 1262745 - Fix tests for Canvas CSS/SVG Filters. r=mstange (93d3652ac0) - Bug 1265131, part 4 - fix tests for Skia m51 update. r=jrmuizel (964ea5c037) - Bug 1265131, part 5 - update Skia to m51 branch. r=jrmuizel (42da76e40e) - Bug 1268816 - allow Skia to use C++11 features on platforms that have them. r=froydnj (ff7d9e46b6) - Bug 1268816 - follow-up to fix #ifdef -> #if. r=me (b81d86c173) - Bug 1267180. Don't draw emojis as paths when they are too big. r=lsalzman (61c3bd732c) - Bug 1269247 - check that SkPaint has a typeface before using it. r=mchang (cf873c19b0) - bug 1249738 - make sScreenConfigurationObservers a function static r=dhylands (ab698385c4) - bug 1249738 - make sBatteryObservers a function static r=dhylands (eb205b1b64) - bits of Bug 1265131, part 5 (1340103927) - Bug 1248224 - backport of Skia GrPathUtils::QuadUVMatrix assertion fix. r=jrmuizel (bcf81bf241) - bug 1249738 - make sNetworkObservers a function static r=dhylands (a5d03d4425)
1228 lines
41 KiB
C++
1228 lines
41 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCSPParser.h"
|
|
#include "nsCSPUtils.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsIStringBundle.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "mozilla/net/ReferrerPolicy.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
static LogModule*
|
|
GetCspParserLog()
|
|
{
|
|
static LazyLogModule gCspParserPRLog("CSPParser");
|
|
return gCspParserPRLog;
|
|
}
|
|
|
|
#define CSPPARSERLOG(args) MOZ_LOG(GetCspParserLog(), mozilla::LogLevel::Debug, args)
|
|
#define CSPPARSERLOGENABLED() MOZ_LOG_TEST(GetCspParserLog(), mozilla::LogLevel::Debug)
|
|
|
|
static const char16_t COLON = ':';
|
|
static const char16_t SEMICOLON = ';';
|
|
static const char16_t SLASH = '/';
|
|
static const char16_t PLUS = '+';
|
|
static const char16_t DASH = '-';
|
|
static const char16_t DOT = '.';
|
|
static const char16_t UNDERLINE = '_';
|
|
static const char16_t TILDE = '~';
|
|
static const char16_t WILDCARD = '*';
|
|
static const char16_t SINGLEQUOTE = '\'';
|
|
static const char16_t OPEN_CURL = '{';
|
|
static const char16_t CLOSE_CURL = '}';
|
|
static const char16_t NUMBER_SIGN = '#';
|
|
static const char16_t QUESTIONMARK = '?';
|
|
static const char16_t PERCENT_SIGN = '%';
|
|
static const char16_t EXCLAMATION = '!';
|
|
static const char16_t DOLLAR = '$';
|
|
static const char16_t AMPERSAND = '&';
|
|
static const char16_t OPENBRACE = '(';
|
|
static const char16_t CLOSINGBRACE = ')';
|
|
static const char16_t EQUALS = '=';
|
|
static const char16_t ATSYMBOL = '@';
|
|
|
|
static const uint32_t kSubHostPathCharacterCutoff = 512;
|
|
|
|
static const char *const kHashSourceValidFns [] = { "sha256", "sha384", "sha512" };
|
|
static const uint32_t kHashSourceValidFnsLen = 3;
|
|
|
|
/* ===== nsCSPTokenizer ==================== */
|
|
|
|
nsCSPTokenizer::nsCSPTokenizer(const char16_t* aStart,
|
|
const char16_t* aEnd)
|
|
: mCurChar(aStart)
|
|
, mEndChar(aEnd)
|
|
{
|
|
CSPPARSERLOG(("nsCSPTokenizer::nsCSPTokenizer"));
|
|
}
|
|
|
|
nsCSPTokenizer::~nsCSPTokenizer()
|
|
{
|
|
CSPPARSERLOG(("nsCSPTokenizer::~nsCSPTokenizer"));
|
|
}
|
|
|
|
void
|
|
nsCSPTokenizer::generateNextToken()
|
|
{
|
|
skipWhiteSpaceAndSemicolon();
|
|
while (!atEnd() &&
|
|
!nsContentUtils::IsHTMLWhitespace(*mCurChar) &&
|
|
*mCurChar != SEMICOLON) {
|
|
mCurToken.Append(*mCurChar++);
|
|
}
|
|
CSPPARSERLOG(("nsCSPTokenizer::generateNextToken: %s", NS_ConvertUTF16toUTF8(mCurToken).get()));
|
|
}
|
|
|
|
void
|
|
nsCSPTokenizer::generateTokens(cspTokens& outTokens)
|
|
{
|
|
CSPPARSERLOG(("nsCSPTokenizer::generateTokens"));
|
|
|
|
// dirAndSrcs holds one set of [ name, src, src, src, ... ]
|
|
nsTArray <nsString> dirAndSrcs;
|
|
|
|
while (!atEnd()) {
|
|
generateNextToken();
|
|
dirAndSrcs.AppendElement(mCurToken);
|
|
skipWhiteSpace();
|
|
if (atEnd() || accept(SEMICOLON)) {
|
|
outTokens.AppendElement(dirAndSrcs);
|
|
dirAndSrcs.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsCSPTokenizer::tokenizeCSPPolicy(const nsAString &aPolicyString,
|
|
cspTokens& outTokens)
|
|
{
|
|
CSPPARSERLOG(("nsCSPTokenizer::tokenizeCSPPolicy"));
|
|
|
|
nsCSPTokenizer tokenizer(aPolicyString.BeginReading(),
|
|
aPolicyString.EndReading());
|
|
|
|
tokenizer.generateTokens(outTokens);
|
|
}
|
|
|
|
/* ===== nsCSPParser ==================== */
|
|
|
|
nsCSPParser::nsCSPParser(cspTokens& aTokens,
|
|
nsIURI* aSelfURI,
|
|
nsCSPContext* aCSPContext,
|
|
bool aDeliveredViaMetaTag)
|
|
: mCurChar(nullptr)
|
|
, mEndChar(nullptr)
|
|
, mHasHashOrNonce(false)
|
|
, mUnsafeInlineKeywordSrc(nullptr)
|
|
, mChildSrc(nullptr)
|
|
, mFrameSrc(nullptr)
|
|
, mTokens(aTokens)
|
|
, mSelfURI(aSelfURI)
|
|
, mPolicy(nullptr)
|
|
, mCSPContext(aCSPContext)
|
|
, mDeliveredViaMetaTag(aDeliveredViaMetaTag)
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::nsCSPParser"));
|
|
}
|
|
|
|
nsCSPParser::~nsCSPParser()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::~nsCSPParser"));
|
|
}
|
|
|
|
static bool
|
|
isCharacterToken(char16_t aSymbol)
|
|
{
|
|
return (aSymbol >= 'a' && aSymbol <= 'z') ||
|
|
(aSymbol >= 'A' && aSymbol <= 'Z');
|
|
}
|
|
|
|
static bool
|
|
isNumberToken(char16_t aSymbol)
|
|
{
|
|
return (aSymbol >= '0' && aSymbol <= '9');
|
|
}
|
|
|
|
static bool
|
|
isValidHexDig(char16_t aHexDig)
|
|
{
|
|
return (isNumberToken(aHexDig) ||
|
|
(aHexDig >= 'A' && aHexDig <= 'F') ||
|
|
(aHexDig >= 'a' && aHexDig <= 'f'));
|
|
}
|
|
|
|
void
|
|
nsCSPParser::resetCurChar(const nsAString& aToken)
|
|
{
|
|
mCurChar = aToken.BeginReading();
|
|
mEndChar = aToken.EndReading();
|
|
resetCurValue();
|
|
}
|
|
|
|
// The path is terminated by the first question mark ("?") or
|
|
// number sign ("#") character, or by the end of the URI.
|
|
// http://tools.ietf.org/html/rfc3986#section-3.3
|
|
bool
|
|
nsCSPParser::atEndOfPath()
|
|
{
|
|
return (atEnd() || peek(QUESTIONMARK) || peek(NUMBER_SIGN));
|
|
}
|
|
|
|
void
|
|
nsCSPParser::percentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
|
|
{
|
|
outDecStr.Truncate();
|
|
|
|
// helper function that should not be visible outside this methods scope
|
|
struct local {
|
|
static inline char16_t convertHexDig(char16_t aHexDig) {
|
|
if (isNumberToken(aHexDig)) {
|
|
return aHexDig - '0';
|
|
}
|
|
if (aHexDig >= 'A' && aHexDig <= 'F') {
|
|
return aHexDig - 'A' + 10;
|
|
}
|
|
// must be a lower case character
|
|
// (aHexDig >= 'a' && aHexDig <= 'f')
|
|
return aHexDig - 'a' + 10;
|
|
}
|
|
};
|
|
|
|
const char16_t *cur, *end, *hexDig1, *hexDig2;
|
|
cur = aEncStr.BeginReading();
|
|
end = aEncStr.EndReading();
|
|
|
|
while (cur != end) {
|
|
// if it's not a percent sign then there is
|
|
// nothing to do for that character
|
|
if (*cur != PERCENT_SIGN) {
|
|
outDecStr.Append(*cur);
|
|
cur++;
|
|
continue;
|
|
}
|
|
|
|
// get the two hexDigs following the '%'-sign
|
|
hexDig1 = cur + 1;
|
|
hexDig2 = cur + 2;
|
|
|
|
// if there are no hexdigs after the '%' then
|
|
// there is nothing to do for us.
|
|
if (hexDig1 == end || hexDig2 == end ||
|
|
!isValidHexDig(*hexDig1) ||
|
|
!isValidHexDig(*hexDig2)) {
|
|
outDecStr.Append(PERCENT_SIGN);
|
|
cur++;
|
|
continue;
|
|
}
|
|
|
|
// decode "% hexDig1 hexDig2" into a character.
|
|
char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
|
|
local::convertHexDig(*hexDig2);
|
|
outDecStr.Append(decChar);
|
|
|
|
// increment 'cur' to after the second hexDig
|
|
cur = ++hexDig2;
|
|
}
|
|
}
|
|
|
|
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
|
bool
|
|
nsCSPParser::atValidUnreservedChar()
|
|
{
|
|
return (peek(isCharacterToken) || peek(isNumberToken) ||
|
|
peek(DASH) || peek(DOT) ||
|
|
peek(UNDERLINE) || peek(TILDE));
|
|
}
|
|
|
|
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
|
// / "*" / "+" / "," / ";" / "="
|
|
// Please note that even though ',' and ';' appear to be
|
|
// valid sub-delims according to the RFC production of paths,
|
|
// both can not appear here by itself, they would need to be
|
|
// pct-encoded in order to be part of the path.
|
|
bool
|
|
nsCSPParser::atValidSubDelimChar()
|
|
{
|
|
return (peek(EXCLAMATION) || peek(DOLLAR) || peek(AMPERSAND) ||
|
|
peek(SINGLEQUOTE) || peek(OPENBRACE) || peek(CLOSINGBRACE) ||
|
|
peek(WILDCARD) || peek(PLUS) || peek(EQUALS));
|
|
}
|
|
|
|
// pct-encoded = "%" HEXDIG HEXDIG
|
|
bool
|
|
nsCSPParser::atValidPctEncodedChar()
|
|
{
|
|
const char16_t* pctCurChar = mCurChar;
|
|
|
|
if ((pctCurChar + 2) >= mEndChar) {
|
|
// string too short, can't be a valid pct-encoded char.
|
|
return false;
|
|
}
|
|
|
|
// Any valid pct-encoding must follow the following format:
|
|
// "% HEXDIG HEXDIG"
|
|
if (PERCENT_SIGN != *pctCurChar ||
|
|
!isValidHexDig(*(pctCurChar+1)) ||
|
|
!isValidHexDig(*(pctCurChar+2))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
// http://tools.ietf.org/html/rfc3986#section-3.3
|
|
bool
|
|
nsCSPParser::atValidPathChar()
|
|
{
|
|
return (atValidUnreservedChar() ||
|
|
atValidSubDelimChar() ||
|
|
atValidPctEncodedChar() ||
|
|
peek(COLON) || peek(ATSYMBOL));
|
|
}
|
|
|
|
void
|
|
nsCSPParser::logWarningErrorToConsole(uint32_t aSeverityFlag,
|
|
const char* aProperty,
|
|
const char16_t* aParams[],
|
|
uint32_t aParamsLength)
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::logWarningErrorToConsole: %s", aProperty));
|
|
// send console messages off to the context and let the context
|
|
// deal with it (potentially messages need to be queued up)
|
|
mCSPContext->logToConsole(NS_ConvertUTF8toUTF16(aProperty).get(),
|
|
aParams,
|
|
aParamsLength,
|
|
EmptyString(), // aSourceName
|
|
EmptyString(), // aSourceLine
|
|
0, // aLineNumber
|
|
0, // aColumnNumber
|
|
aSeverityFlag); // aFlags
|
|
}
|
|
|
|
bool
|
|
nsCSPParser::hostChar()
|
|
{
|
|
if (atEnd()) {
|
|
return false;
|
|
}
|
|
return accept(isCharacterToken) ||
|
|
accept(isNumberToken) ||
|
|
accept(DASH);
|
|
}
|
|
|
|
// (ALPHA / DIGIT / "+" / "-" / "." )
|
|
bool
|
|
nsCSPParser::schemeChar()
|
|
{
|
|
if (atEnd()) {
|
|
return false;
|
|
}
|
|
return accept(isCharacterToken) ||
|
|
accept(isNumberToken) ||
|
|
accept(PLUS) ||
|
|
accept(DASH) ||
|
|
accept(DOT);
|
|
}
|
|
|
|
// port = ":" ( 1*DIGIT / "*" )
|
|
bool
|
|
nsCSPParser::port()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::port, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Consume the COLON we just peeked at in houstSource
|
|
accept(COLON);
|
|
|
|
// Resetting current value since we start to parse a port now.
|
|
// e.g; "http://www.example.com:8888" then we have already parsed
|
|
// everything up to (including) ":";
|
|
resetCurValue();
|
|
|
|
// Port might be "*"
|
|
if (accept(WILDCARD)) {
|
|
return true;
|
|
}
|
|
|
|
// Port must start with a number
|
|
if (!accept(isNumberToken)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParsePort",
|
|
params, ArrayLength(params));
|
|
return false;
|
|
}
|
|
// Consume more numbers and set parsed port to the nsCSPHost
|
|
while (accept(isNumberToken)) { /* consume */ }
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
nsCSPParser::subPath(nsCSPHostSrc* aCspHost)
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::subPath, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Emergency exit to avoid endless loops in case a path in a CSP policy
|
|
// is longer than 512 characters, or also to avoid endless loops
|
|
// in case we are parsing unrecognized characters in the following loop.
|
|
uint32_t charCounter = 0;
|
|
nsString pctDecodedSubPath;
|
|
|
|
while (!atEndOfPath()) {
|
|
if (peek(SLASH)) {
|
|
// before appendig any additional portion of a subpath we have to pct-decode
|
|
// that portion of the subpath. atValidPathChar() already verified a correct
|
|
// pct-encoding, now we can safely decode and append the decoded-sub path.
|
|
percentDecodeStr(mCurValue, pctDecodedSubPath);
|
|
aCspHost->appendPath(pctDecodedSubPath);
|
|
// Resetting current value since we are appending parts of the path
|
|
// to aCspHost, e.g; "http://www.example.com/path1/path2" then the
|
|
// first part is "/path1", second part "/path2"
|
|
resetCurValue();
|
|
}
|
|
else if (!atValidPathChar()) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
|
"couldntParseInvalidSource",
|
|
params, ArrayLength(params));
|
|
return false;
|
|
}
|
|
// potentially we have encountred a valid pct-encoded character in atValidPathChar();
|
|
// if so, we have to account for "% HEXDIG HEXDIG" and advance the pointer past
|
|
// the pct-encoded char.
|
|
if (peek(PERCENT_SIGN)) {
|
|
advance();
|
|
advance();
|
|
}
|
|
advance();
|
|
if (++charCounter > kSubHostPathCharacterCutoff) {
|
|
return false;
|
|
}
|
|
}
|
|
// before appendig any additional portion of a subpath we have to pct-decode
|
|
// that portion of the subpath. atValidPathChar() already verified a correct
|
|
// pct-encoding, now we can safely decode and append the decoded-sub path.
|
|
percentDecodeStr(mCurValue, pctDecodedSubPath);
|
|
aCspHost->appendPath(pctDecodedSubPath);
|
|
resetCurValue();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
nsCSPParser::path(nsCSPHostSrc* aCspHost)
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::path, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Resetting current value and forgetting everything we have parsed so far
|
|
// e.g. parsing "http://www.example.com/path1/path2", then
|
|
// "http://www.example.com" has already been parsed so far
|
|
// forget about it.
|
|
resetCurValue();
|
|
|
|
if (!accept(SLASH)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource",
|
|
params, ArrayLength(params));
|
|
return false;
|
|
}
|
|
if (atEndOfPath()) {
|
|
// one slash right after host [port] is also considered a path, e.g.
|
|
// www.example.com/ should result in www.example.com/
|
|
// please note that we do not have to perform any pct-decoding here
|
|
// because we are just appending a '/' and not any actual chars.
|
|
aCspHost->appendPath(NS_LITERAL_STRING("/"));
|
|
return true;
|
|
}
|
|
// path can begin with "/" but not "//"
|
|
// see http://tools.ietf.org/html/rfc3986#section-3.3
|
|
if (peek(SLASH)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource",
|
|
params, ArrayLength(params));
|
|
return false;
|
|
}
|
|
return subPath(aCspHost);
|
|
}
|
|
|
|
bool
|
|
nsCSPParser::subHost()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::subHost, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Emergency exit to avoid endless loops in case a host in a CSP policy
|
|
// is longer than 512 characters, or also to avoid endless loops
|
|
// in case we are parsing unrecognized characters in the following loop.
|
|
uint32_t charCounter = 0;
|
|
|
|
while (!atEndOfPath() && !peek(COLON) && !peek(SLASH)) {
|
|
++charCounter;
|
|
while (hostChar()) {
|
|
/* consume */
|
|
++charCounter;
|
|
}
|
|
if (accept(DOT) && !hostChar()) {
|
|
return false;
|
|
}
|
|
if (charCounter > kSubHostPathCharacterCutoff) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// host = "*" / [ "*." ] 1*host-char *( "." 1*host-char )
|
|
nsCSPHostSrc*
|
|
nsCSPParser::host()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::host, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Check if the token starts with "*"; please remember that we handle
|
|
// a single "*" as host in sourceExpression, but we still have to handle
|
|
// the case where a scheme was defined, e.g., as:
|
|
// "https://*", "*.example.com", "*:*", etc.
|
|
if (accept(WILDCARD)) {
|
|
// Might solely be the wildcard
|
|
if (atEnd() || peek(COLON)) {
|
|
return new nsCSPHostSrc(mCurValue);
|
|
}
|
|
// If the token is not only the "*", a "." must follow right after
|
|
if (!accept(DOT)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Expecting at least one host-char
|
|
if (!hostChar()) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
|
|
// There might be several sub hosts defined.
|
|
if (!subHost()) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
|
|
// HostName might match a keyword, log to the console.
|
|
if (CSP_IsQuotelessKeyword(mCurValue)) {
|
|
nsString keyword = mCurValue;
|
|
ToLowerCase(keyword);
|
|
const char16_t* params[] = { mCurToken.get(), keyword.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "hostNameMightBeKeyword",
|
|
params, ArrayLength(params));
|
|
}
|
|
|
|
// Create a new nsCSPHostSrc with the parsed host.
|
|
return new nsCSPHostSrc(mCurValue);
|
|
}
|
|
|
|
// apps use special hosts; "app://{app-host-is-uid}""
|
|
nsCSPHostSrc*
|
|
nsCSPParser::appHost()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::appHost, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
while (hostChar()) { /* consume */ }
|
|
|
|
// appHosts have to end with "}", otherwise we have to report an error
|
|
if (!accept(CLOSE_CURL)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
return new nsCSPHostSrc(mCurValue);
|
|
}
|
|
|
|
// keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
|
|
nsCSPBaseSrc*
|
|
nsCSPParser::keywordSource()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::keywordSource, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Special case handling for 'self' which is not stored internally as a keyword,
|
|
// but rather creates a nsCSPHostSrc using the selfURI
|
|
if (CSP_IsKeyword(mCurToken, CSP_SELF)) {
|
|
return CSP_CreateHostSrcFromURI(mSelfURI);
|
|
}
|
|
|
|
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
|
|
nsWeakPtr ctx = mCSPContext->GetLoadingContext();
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(ctx);
|
|
if (doc) {
|
|
doc->SetHasUnsafeInlineCSP(true);
|
|
}
|
|
// make sure script-src only contains 'unsafe-inline' once;
|
|
// ignore duplicates and log warning
|
|
if (mUnsafeInlineKeywordSrc) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDuplicateSrc",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
// cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
|
|
// case that script-src directive also contains hash- or nonce-.
|
|
mUnsafeInlineKeywordSrc = new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
|
|
return mUnsafeInlineKeywordSrc;
|
|
}
|
|
|
|
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
|
|
nsWeakPtr ctx = mCSPContext->GetLoadingContext();
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(ctx);
|
|
if (doc) {
|
|
doc->SetHasUnsafeEvalCSP(true);
|
|
}
|
|
return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// host-source = [ scheme "://" ] host [ port ] [ path ]
|
|
nsCSPHostSrc*
|
|
nsCSPParser::hostSource()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::hostSource, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Special case handling for app specific hosts
|
|
if (accept(OPEN_CURL)) {
|
|
// If appHost() returns null, the error was handled in appHost().
|
|
// appHosts can not have a port, or path, we can return.
|
|
return appHost();
|
|
}
|
|
|
|
nsCSPHostSrc* cspHost = host();
|
|
if (!cspHost) {
|
|
// Error was reported in host()
|
|
return nullptr;
|
|
}
|
|
|
|
// Calling port() to see if there is a port to parse, if an error
|
|
// occurs, port() reports the error, if port() returns true;
|
|
// we have a valid port, so we add it to cspHost.
|
|
if (peek(COLON)) {
|
|
if (!port()) {
|
|
delete cspHost;
|
|
return nullptr;
|
|
}
|
|
cspHost->setPort(mCurValue);
|
|
}
|
|
|
|
if (atEndOfPath()) {
|
|
return cspHost;
|
|
}
|
|
|
|
// Calling path() to see if there is a path to parse, if an error
|
|
// occurs, path() reports the error; handing cspHost as an argument
|
|
// which simplifies parsing of several paths.
|
|
if (!path(cspHost)) {
|
|
// If the host [port] is followed by a path, it has to be a valid path,
|
|
// otherwise we pass the nullptr, indicating an error, up the callstack.
|
|
// see also http://www.w3.org/TR/CSP11/#source-list
|
|
delete cspHost;
|
|
return nullptr;
|
|
}
|
|
return cspHost;
|
|
}
|
|
|
|
// scheme-source = scheme ":"
|
|
nsCSPSchemeSrc*
|
|
nsCSPParser::schemeSource()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::schemeSource, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
if (!accept(isCharacterToken)) {
|
|
return nullptr;
|
|
}
|
|
while (schemeChar()) { /* consume */ }
|
|
nsString scheme = mCurValue;
|
|
|
|
// If the potential scheme is not followed by ":" - it's not a scheme
|
|
if (!accept(COLON)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// If the chraracter following the ":" is a number or the "*"
|
|
// then we are not parsing a scheme; but rather a host;
|
|
if (peek(isNumberToken) || peek(WILDCARD)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return new nsCSPSchemeSrc(scheme);
|
|
}
|
|
|
|
// nonce-source = "'nonce-" nonce-value "'"
|
|
nsCSPNonceSrc*
|
|
nsCSPParser::nonceSource()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::nonceSource, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Check if mCurToken begins with "'nonce-" and ends with "'"
|
|
if (!StringBeginsWith(mCurToken, NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONCE)),
|
|
nsASCIICaseInsensitiveStringComparator()) ||
|
|
mCurToken.Last() != SINGLEQUOTE) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Trim surrounding single quotes
|
|
const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
|
|
|
|
int32_t dashIndex = expr.FindChar(DASH);
|
|
if (dashIndex < 0) {
|
|
return nullptr;
|
|
}
|
|
// cache if encountering hash or nonce to invalidate unsafe-inline
|
|
mHasHashOrNonce = true;
|
|
return new nsCSPNonceSrc(Substring(expr,
|
|
dashIndex + 1,
|
|
expr.Length() - dashIndex + 1));
|
|
}
|
|
|
|
// hash-source = "'" hash-algo "-" base64-value "'"
|
|
nsCSPHashSrc*
|
|
nsCSPParser::hashSource()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::hashSource, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Check if mCurToken starts and ends with "'"
|
|
if (mCurToken.First() != SINGLEQUOTE ||
|
|
mCurToken.Last() != SINGLEQUOTE) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Trim surrounding single quotes
|
|
const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
|
|
|
|
int32_t dashIndex = expr.FindChar(DASH);
|
|
if (dashIndex < 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsAutoString algo(Substring(expr, 0, dashIndex));
|
|
nsAutoString hash(Substring(expr, dashIndex + 1, expr.Length() - dashIndex + 1));
|
|
|
|
for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) {
|
|
if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) {
|
|
// cache if encountering hash or nonce to invalidate unsafe-inline
|
|
mHasHashOrNonce = true;
|
|
return new nsCSPHashSrc(algo, hash);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// source-expression = scheme-source / host-source / keyword-source
|
|
// / nonce-source / hash-source
|
|
nsCSPBaseSrc*
|
|
nsCSPParser::sourceExpression()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::sourceExpression, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Check if it is a keyword
|
|
if (nsCSPBaseSrc *cspKeyword = keywordSource()) {
|
|
return cspKeyword;
|
|
}
|
|
|
|
// Check if it is a nonce-source
|
|
if (nsCSPNonceSrc* cspNonce = nonceSource()) {
|
|
return cspNonce;
|
|
}
|
|
|
|
// Check if it is a hash-source
|
|
if (nsCSPHashSrc* cspHash = hashSource()) {
|
|
return cspHash;
|
|
}
|
|
|
|
// We handle a single "*" as host here, to avoid any confusion when applying the default scheme.
|
|
// However, we still would need to apply the default scheme in case we would parse "*:80".
|
|
if (mCurToken.EqualsASCII("*")) {
|
|
return new nsCSPHostSrc(NS_LITERAL_STRING("*"));
|
|
}
|
|
|
|
// Calling resetCurChar allows us to use mCurChar and mEndChar
|
|
// to parse mCurToken; e.g. mCurToken = "http://www.example.com", then
|
|
// mCurChar = 'h'
|
|
// mEndChar = points just after the last 'm'
|
|
// mCurValue = ""
|
|
resetCurChar(mCurToken);
|
|
|
|
// Check if mCurToken starts with a scheme
|
|
nsAutoString parsedScheme;
|
|
if (nsCSPSchemeSrc* cspScheme = schemeSource()) {
|
|
// mCurToken might only enforce a specific scheme
|
|
if (atEnd()) {
|
|
return cspScheme;
|
|
}
|
|
// If something follows the scheme, we do not create
|
|
// a nsCSPSchemeSrc, but rather a nsCSPHostSrc, which
|
|
// needs to know the scheme to enforce; remember the
|
|
// scheme and delete cspScheme;
|
|
cspScheme->toString(parsedScheme);
|
|
parsedScheme.Trim(":", false, true);
|
|
delete cspScheme;
|
|
|
|
// If mCurToken provides not only a scheme, but also a host, we have to check
|
|
// if two slashes follow the scheme.
|
|
if (!accept(SLASH) || !accept(SLASH)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar
|
|
// alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com", then
|
|
// mCurChar = 'w'
|
|
// mEndChar = 'm'
|
|
// mCurValue = ""
|
|
resetCurValue();
|
|
|
|
// If mCurToken does not provide a scheme (scheme-less source), we apply the scheme
|
|
// from selfURI
|
|
if (parsedScheme.IsEmpty()) {
|
|
// Resetting internal helpers, because we might already have parsed some of the host
|
|
// when trying to parse a scheme.
|
|
resetCurChar(mCurToken);
|
|
nsAutoCString selfScheme;
|
|
mSelfURI->GetScheme(selfScheme);
|
|
parsedScheme.AssignASCII(selfScheme.get());
|
|
}
|
|
|
|
// At this point we are expecting a host to be parsed.
|
|
// Trying to create a new nsCSPHost.
|
|
if (nsCSPHostSrc *cspHost = hostSource()) {
|
|
// Do not forget to set the parsed scheme.
|
|
cspHost->setScheme(parsedScheme);
|
|
return cspHost;
|
|
}
|
|
// Error was reported in hostSource()
|
|
return nullptr;
|
|
}
|
|
|
|
// source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
|
|
// / *WSP "'none'" *WSP
|
|
void
|
|
nsCSPParser::sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
|
{
|
|
bool isNone = false;
|
|
|
|
// remember, srcs start at index 1
|
|
for (uint32_t i = 1; i < mCurDir.Length(); i++) {
|
|
// mCurToken is only set here and remains the current token
|
|
// to be processed, which avoid passing arguments between functions.
|
|
mCurToken = mCurDir[i];
|
|
resetCurValue();
|
|
|
|
CSPPARSERLOG(("nsCSPParser::sourceList, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Special case handling for none:
|
|
// Ignore 'none' if any other src is available.
|
|
// (See http://www.w3.org/TR/CSP11/#parsing)
|
|
if (CSP_IsKeyword(mCurToken, CSP_NONE)) {
|
|
isNone = true;
|
|
continue;
|
|
}
|
|
// Must be a regular source expression
|
|
nsCSPBaseSrc* src = sourceExpression();
|
|
if (src) {
|
|
outSrcs.AppendElement(src);
|
|
}
|
|
}
|
|
|
|
// Check if the directive contains a 'none'
|
|
if (isNone) {
|
|
// If the directive contains no other srcs, then we set the 'none'
|
|
if (outSrcs.Length() == 0) {
|
|
nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
|
|
outSrcs.AppendElement(keyword);
|
|
}
|
|
// Otherwise, we ignore 'none' and report a warning
|
|
else {
|
|
NS_ConvertUTF8toUTF16 unicodeNone(CSP_EnumToKeyword(CSP_NONE));
|
|
const char16_t* params[] = { unicodeNone.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
|
|
params, ArrayLength(params));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsCSPParser::referrerDirectiveValue(nsCSPDirective* aDir)
|
|
{
|
|
// directive-value = "none" / "none-when-downgrade" / "origin" / "origin-when-cross-origin" / "unsafe-url"
|
|
// directive name is token 0, we need to examine the remaining tokens (and
|
|
// there should only be one token in the value).
|
|
CSPPARSERLOG(("nsCSPParser::referrerDirectiveValue"));
|
|
|
|
if (mCurDir.Length() != 2) {
|
|
CSPPARSERLOG(("Incorrect number of tokens in referrer directive, got %d expected 1",
|
|
mCurDir.Length() - 1));
|
|
delete aDir;
|
|
return;
|
|
}
|
|
|
|
if (!mozilla::net::IsValidReferrerPolicy(mCurDir[1])) {
|
|
CSPPARSERLOG(("invalid value for referrer directive: %s",
|
|
NS_ConvertUTF16toUTF8(mCurDir[1]).get()));
|
|
delete aDir;
|
|
return;
|
|
}
|
|
|
|
// the referrer policy is valid, so go ahead and use it.
|
|
mPolicy->setReferrerPolicy(&mCurDir[1]);
|
|
mPolicy->addDirective(aDir);
|
|
}
|
|
|
|
void
|
|
nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
|
{
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv;
|
|
|
|
// remember, srcs start at index 1
|
|
for (uint32_t i = 1; i < mCurDir.Length(); i++) {
|
|
mCurToken = mCurDir[i];
|
|
|
|
CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
rv = NS_NewURI(getter_AddRefs(uri), mCurToken, "", mSelfURI);
|
|
|
|
// If creating the URI casued an error, skip this URI
|
|
if (NS_FAILED(rv)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotParseReportURI",
|
|
params, ArrayLength(params));
|
|
continue;
|
|
}
|
|
|
|
// Create new nsCSPReportURI and append to the list.
|
|
nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
|
|
outSrcs.AppendElement(reportURI);
|
|
}
|
|
}
|
|
|
|
// directive-value = *( WSP / <VCHAR except ";" and ","> )
|
|
void
|
|
nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::directiveValue"));
|
|
|
|
// The tokenzier already generated an array in the form of
|
|
// [ name, src, src, ... ], no need to parse again, but
|
|
// special case handling in case the directive is report-uri.
|
|
if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
|
|
reportURIList(outSrcs);
|
|
return;
|
|
}
|
|
|
|
// Otherwise just forward to sourceList
|
|
sourceList(outSrcs);
|
|
}
|
|
|
|
// directive-name = 1*( ALPHA / DIGIT / "-" )
|
|
nsCSPDirective*
|
|
nsCSPParser::directiveName()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Check if it is a valid directive
|
|
if (!CSP_IsValidDirective(mCurToken)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotProcessUnknownDirective",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
|
|
// The directive 'reflected-xss' is part of CSP 1.1, see:
|
|
// http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
|
|
// Currently we are not supporting that directive, hence we log a
|
|
// warning to the console and ignore the directive including its values.
|
|
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE)) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "notSupportingDirective",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
|
|
// Make sure the directive does not already exist
|
|
// (see http://www.w3.org/TR/CSP11/#parsing)
|
|
if (mPolicy->hasDirective(CSP_StringToCSPDirective(mCurToken))) {
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
|
|
// CSP delivered via meta tag should ignore the following directives:
|
|
// report-uri, frame-ancestors, and sandbox, see:
|
|
// http://www.w3.org/TR/CSP11/#delivery-html-meta-element
|
|
if (mDeliveredViaMetaTag &&
|
|
((CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) ||
|
|
(CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)))) {
|
|
// log to the console to indicate that meta CSP is ignoring the directive
|
|
const char16_t* params[] = { mCurToken.get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
|
"ignoringSrcFromMetaCSP",
|
|
params, ArrayLength(params));
|
|
return nullptr;
|
|
}
|
|
|
|
// special case handling for block-all-mixed-content
|
|
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
|
|
return new nsBlockAllMixedContentDirective(CSP_StringToCSPDirective(mCurToken));
|
|
}
|
|
|
|
// special case handling for upgrade-insecure-requests
|
|
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
|
|
return new nsUpgradeInsecureDirective(CSP_StringToCSPDirective(mCurToken));
|
|
}
|
|
|
|
// child-src has it's own class to handle frame-src if necessary
|
|
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE)) {
|
|
mChildSrc = new nsCSPChildSrcDirective(CSP_StringToCSPDirective(mCurToken));
|
|
return mChildSrc;
|
|
}
|
|
|
|
// if we have a frame-src, cache it so we can decide whether to use child-src
|
|
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE)) {
|
|
const char16_t* params[] = { mCurToken.get(), NS_LITERAL_STRING("child-src").get() };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "deprecatedDirective",
|
|
params, ArrayLength(params));
|
|
mFrameSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
|
|
return mFrameSrc;
|
|
}
|
|
|
|
return new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
|
|
}
|
|
|
|
// directive = *WSP [ directive-name [ WSP directive-value ] ]
|
|
void
|
|
nsCSPParser::directive()
|
|
{
|
|
// Set the directiveName to mCurToken
|
|
// Remember, the directive name is stored at index 0
|
|
mCurToken = mCurDir[0];
|
|
|
|
CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
|
|
NS_ConvertUTF16toUTF8(mCurToken).get(),
|
|
NS_ConvertUTF16toUTF8(mCurValue).get()));
|
|
|
|
// Make sure that the directive-srcs-array contains at least
|
|
// one directive and one src.
|
|
if (mCurDir.Length() < 1) {
|
|
const char16_t* params[] = { MOZ_UTF16("directive missing") };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
|
|
params, ArrayLength(params));
|
|
return;
|
|
}
|
|
|
|
// Try to create a new CSPDirective
|
|
nsCSPDirective* cspDir = directiveName();
|
|
if (!cspDir) {
|
|
// if we can not create a CSPDirective, we can skip parsing the srcs for that array
|
|
return;
|
|
}
|
|
|
|
// special case handling for block-all-mixed-content, which is only specified
|
|
// by a directive name but does not include any srcs.
|
|
if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
|
|
if (mCurDir.Length() > 1) {
|
|
const char16_t* params[] = { MOZ_UTF16("block-all-mixed-content") };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
|
"ignoreSrcForDirective",
|
|
params, ArrayLength(params));
|
|
}
|
|
// add the directive and return
|
|
mPolicy->addDirective(cspDir);
|
|
return;
|
|
}
|
|
|
|
// special case handling for upgrade-insecure-requests, which is only specified
|
|
// by a directive name but does not include any srcs.
|
|
if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
|
|
if (mCurDir.Length() > 1) {
|
|
const char16_t* params[] = { MOZ_UTF16("upgrade-insecure-requests") };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
|
"ignoreSrcForDirective",
|
|
params, ArrayLength(params));
|
|
}
|
|
// add the directive and return
|
|
mPolicy->addUpgradeInsecDir(static_cast<nsUpgradeInsecureDirective*>(cspDir));
|
|
return;
|
|
}
|
|
|
|
// special case handling of the referrer directive (since it doesn't contain
|
|
// source lists)
|
|
if (cspDir->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
|
|
referrerDirectiveValue(cspDir);
|
|
return;
|
|
}
|
|
|
|
// make sure to reset cache variables when trying to invalidate unsafe-inline;
|
|
// unsafe-inline might not only appear in script-src, but also in default-src
|
|
mHasHashOrNonce = false;
|
|
mUnsafeInlineKeywordSrc = nullptr;
|
|
|
|
// Try to parse all the srcs by handing the array off to directiveValue
|
|
nsTArray<nsCSPBaseSrc*> srcs;
|
|
directiveValue(srcs);
|
|
|
|
// If we can not parse any srcs; we let the source expression be the empty set ('none')
|
|
// see, http://www.w3.org/TR/CSP11/#source-list-parsing
|
|
if (srcs.Length() == 0) {
|
|
nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
|
|
srcs.AppendElement(keyword);
|
|
}
|
|
|
|
// Ignore unsafe-inline within script-src or style-src if nonce
|
|
// or hash is specified, see:
|
|
// http://www.w3.org/TR/CSP2/#directive-script-src
|
|
if ((cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
|
|
cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE)) &&
|
|
mHasHashOrNonce && mUnsafeInlineKeywordSrc) {
|
|
mUnsafeInlineKeywordSrc->invalidate();
|
|
// log to the console that unsafe-inline will be ignored
|
|
const char16_t* params[] = { MOZ_UTF16("'unsafe-inline'") };
|
|
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptStyleSrc",
|
|
params, ArrayLength(params));
|
|
}
|
|
|
|
// Add the newly created srcs to the directive and add the directive to the policy
|
|
cspDir->addSrcs(srcs);
|
|
mPolicy->addDirective(cspDir);
|
|
}
|
|
|
|
// policy = [ directive *( ";" [ directive ] ) ]
|
|
nsCSPPolicy*
|
|
nsCSPParser::policy()
|
|
{
|
|
CSPPARSERLOG(("nsCSPParser::policy"));
|
|
|
|
mPolicy = new nsCSPPolicy();
|
|
for (uint32_t i = 0; i < mTokens.Length(); i++) {
|
|
// All input is already tokenized; set one tokenized array in the form of
|
|
// [ name, src, src, ... ]
|
|
// to mCurDir and call directive which processes the current directive.
|
|
mCurDir = mTokens[i];
|
|
directive();
|
|
}
|
|
|
|
if (mChildSrc && !mFrameSrc) {
|
|
// if we have a child-src, it handles frame-src too, unless frame-src is set
|
|
mChildSrc->setHandleFrameSrc();
|
|
}
|
|
|
|
return mPolicy;
|
|
}
|
|
|
|
nsCSPPolicy*
|
|
nsCSPParser::parseContentSecurityPolicy(const nsAString& aPolicyString,
|
|
nsIURI *aSelfURI,
|
|
bool aReportOnly,
|
|
nsCSPContext* aCSPContext,
|
|
bool aDeliveredViaMetaTag)
|
|
{
|
|
if (CSPPARSERLOGENABLED()) {
|
|
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, policy: %s",
|
|
NS_ConvertUTF16toUTF8(aPolicyString).get()));
|
|
nsAutoCString spec;
|
|
aSelfURI->GetSpec(spec);
|
|
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, selfURI: %s", spec.get()));
|
|
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, reportOnly: %s",
|
|
(aReportOnly ? "true" : "false")));
|
|
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, deliveredViaMetaTag: %s",
|
|
(aDeliveredViaMetaTag ? "true" : "false")));
|
|
}
|
|
|
|
NS_ASSERTION(aSelfURI, "Can not parseContentSecurityPolicy without aSelfURI");
|
|
|
|
// Separate all input into tokens and store them in the form of:
|
|
// [ [ name, src, src, ... ], [ name, src, src, ... ], ... ]
|
|
// The tokenizer itself can not fail; all eventual errors
|
|
// are detected in the parser itself.
|
|
|
|
nsTArray< nsTArray<nsString> > tokens;
|
|
nsCSPTokenizer::tokenizeCSPPolicy(aPolicyString, tokens);
|
|
|
|
nsCSPParser parser(tokens, aSelfURI, aCSPContext, aDeliveredViaMetaTag);
|
|
|
|
// Start the parser to generate a new CSPPolicy using the generated tokens.
|
|
nsCSPPolicy* policy = parser.policy();
|
|
|
|
// Check that report-only policies define a report-uri, otherwise log warning.
|
|
if (aReportOnly) {
|
|
policy->setReportOnlyFlag(true);
|
|
if (!policy->hasDirective(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
|
|
nsAutoCString prePath;
|
|
nsresult rv = aSelfURI->GetPrePath(prePath);
|
|
NS_ENSURE_SUCCESS(rv, policy);
|
|
NS_ConvertUTF8toUTF16 unicodePrePath(prePath);
|
|
const char16_t* params[] = { unicodePrePath.get() };
|
|
parser.logWarningErrorToConsole(nsIScriptError::warningFlag, "reportURInotInReportOnlyHeader",
|
|
params, ArrayLength(params));
|
|
}
|
|
}
|
|
|
|
if (policy->getNumDirectives() == 0) {
|
|
// Individual errors were already reported in the parser, but if
|
|
// we do not have an enforcable directive at all, we return null.
|
|
delete policy;
|
|
return nullptr;
|
|
}
|
|
|
|
if (CSPPARSERLOGENABLED()) {
|
|
nsString parsedPolicy;
|
|
policy->toString(parsedPolicy);
|
|
CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, parsedPolicy: %s",
|
|
NS_ConvertUTF16toUTF8(parsedPolicy).get()));
|
|
}
|
|
|
|
return policy;
|
|
}
|