1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
/* -*- 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 "core/TelemetryEvent.h"
#include "gtest/gtest.h"
#include "js/Array.h" // JS::GetArrayLength
#include "mozilla/Maybe.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Unused.h"
#include "TelemetryFixture.h"
#include "TelemetryTestHelpers.h"
#include <string.h>
#include <stdlib.h>
#include "nsContentSecurityManager.h"
#include "nsContentSecurityUtils.h"
#include "nsNetUtil.h"
#include "nsStringFwd.h"
using namespace mozilla;
using namespace TelemetryTestHelpers;
TEST_F(TelemetryTestFixture, UnexpectedPrivilegedLoadsTelemetryTest) {
struct testResults {
nsCString fileinfo;
nsCString extraValueContenttype;
nsCString extraValueRemotetype;
nsCString extraValueFiledetails;
};
struct testCasesAndResults {
nsCString urlstring;
ExtContentPolicyType contentType;
nsCString remoteType;
testResults expected;
};
AutoJSContextWithGlobal cx(mCleanGlobal);
// Make sure we don't look at events from other tests.
Unused << mTelemetry->ClearEvents();
// required for telemetry lookups
constexpr auto category = "security"_ns;
constexpr auto method = "unexpectedload"_ns;
constexpr auto object = "systemprincipal"_ns;
constexpr auto extraKeyContenttype = "contenttype"_ns;
constexpr auto extraKeyRemotetype = "remotetype"_ns;
constexpr auto extraKeyFiledetails = "filedetails"_ns;
// some cases from TestFilenameEvalParser
// no need to replicate all scenarios?!
testCasesAndResults myTestCases[] = {
{"chrome://firegestures/content/browser.js"_ns,
ExtContentPolicy::TYPE_SCRIPT,
"web"_ns,
{"chromeuri"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
"chrome://firegestures/content/browser.js"_ns}},
{"resource://firegestures/content/browser.js"_ns,
ExtContentPolicy::TYPE_SCRIPT,
"web"_ns,
{"resourceuri"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
"resource://firegestures/content/browser.js"_ns}},
{// test that we don't report blob details
// ..and test that we strip of URLs from remoteTypes
"blob://000-000"_ns,
ExtContentPolicy::TYPE_SCRIPT,
"webIsolated=https://blob.example/"_ns,
{"bloburi"_ns, "TYPE_SCRIPT"_ns, "webIsolated"_ns, "unknown"_ns}},
{// test for cases where finalURI is null, due to a broken nested URI
// .. like malformed moz-icon URLs
"moz-icon:blahblah"_ns,
ExtContentPolicy::TYPE_IMAGE,
"web"_ns,
{"other"_ns, "TYPE_IMAGE"_ns, "web"_ns, "unknown"_ns}},
{// we dont report data urls
// ..and test that we strip of URLs from remoteTypes
"data://blahblahblah"_ns,
ExtContentPolicy::TYPE_DOCUMENT,
"webCOOP+COEP=https://data.example"_ns,
{"dataurl"_ns, "TYPE_DOCUMENT"_ns, "webCOOP+COEP"_ns, "unknown"_ns}},
{// we only report file URLs on windows, where we can easily sanitize
"file://c/users/tom/file.txt"_ns,
ExtContentPolicy::TYPE_SCRIPT,
"web"_ns,
{
#if defined(XP_WIN)
"sanitizedWindowsURL"_ns, "TYPE_SCRIPT"_ns, "web"_ns,
"file://.../file.txt"_ns
#else
"other"_ns, "TYPE_SCRIPT"_ns, "web"_ns, "unknown"_ns
#endif
}},
{// test for cases where finalURI is empty
""_ns,
ExtContentPolicy::TYPE_IMAGE,
"web"_ns,
{"other"_ns, "TYPE_IMAGE"_ns, "web"_ns, "unknown"_ns}},
{// test for cases where finalURI is null, due to the struct layout, we'll
// override the URL with nullptr in loop below.
"URLWillResultInNullPtr"_ns,
ExtContentPolicy::TYPE_FONT,
"web"_ns,
{"other"_ns, "TYPE_FONT"_ns, "web"_ns, "unknown"_ns}},
};
int i = 0;
for (auto const& currentTest : myTestCases) {
nsCOMPtr<nsIURI> uri;
// special-casing for a case where the uri is null
if (!currentTest.urlstring.Equals("URLWillResultInNullPtr")) {
NS_NewURI(getter_AddRefs(uri), currentTest.urlstring);
}
// this will record the event
nsContentSecurityManager::MeasureUnexpectedPrivilegedLoads(
uri, currentTest.contentType, currentTest.remoteType);
// let's inspect the recorded events
JS::RootedValue eventsSnapshot(cx.GetJSContext());
GetEventSnapshot(cx.GetJSContext(), &eventsSnapshot);
ASSERT_TRUE(EventPresent(cx.GetJSContext(), eventsSnapshot, category,
method, object))
<< "Test event with value and extra must be present.";
// Convert eventsSnapshot into array/object
JSContext* aCx = cx.GetJSContext();
JS::RootedObject arrayObj(aCx, &eventsSnapshot.toObject());
JS::Rooted<JS::Value> eventRecord(aCx);
ASSERT_TRUE(JS_GetElement(aCx, arrayObj, i++, &eventRecord))
<< "Must be able to get record."; // record is already undefined :-/
ASSERT_TRUE(!eventRecord.isUndefined())
<< "eventRecord should not be undefined";
JS::RootedObject recordArray(aCx, &eventRecord.toObject());
uint32_t recordLength;
ASSERT_TRUE(JS::GetArrayLength(aCx, recordArray, &recordLength))
<< "Event record array must have length.";
ASSERT_TRUE(recordLength == 6)
<< "Event record must have 6 elements.";
JS::Rooted<JS::Value> str(aCx);
nsAutoJSString jsStr;
// The fileinfo string is at index 4
ASSERT_TRUE(JS_GetElement(aCx, recordArray, 4, &str))
<< "Must be able to get value.";
ASSERT_TRUE(jsStr.init(aCx, str))
<< "Value must be able to be init'd to a jsstring.";
ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
currentTest.expected.fileinfo.get())
<< "Reported fileinfo equals supplied value";
// Extra is at index 5
JS::Rooted<JS::Value> obj(aCx);
ASSERT_TRUE(JS_GetElement(aCx, recordArray, 5, &obj))
<< "Must be able to get extra.";
JS::RootedObject extraObj(aCx, &obj.toObject());
// looking at remotetype extra for content type
JS::Rooted<JS::Value> extraValC(aCx);
ASSERT_TRUE(
JS_GetProperty(aCx, extraObj, extraKeyContenttype.get(), &extraValC))
<< "Must be able to get the extra key's value for contenttype";
ASSERT_TRUE(jsStr.init(aCx, extraValC))
<< "Extra value contenttype must be able to be init'd to a jsstring.";
ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
currentTest.expected.extraValueContenttype.get())
<< "Reported value for extra contenttype should equals supplied value";
// and again for remote type
JS::Rooted<JS::Value> extraValP(aCx);
ASSERT_TRUE(
JS_GetProperty(aCx, extraObj, extraKeyRemotetype.get(), &extraValP))
<< "Must be able to get the extra key's value for remotetype";
ASSERT_TRUE(jsStr.init(aCx, extraValP))
<< "Extra value remotetype must be able to be init'd to a jsstring.";
ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
currentTest.expected.extraValueRemotetype.get())
<< "Reported value for extra remotetype should equals supplied value";
// repeating the same for filedetails extra
JS::Rooted<JS::Value> extraValF(aCx);
ASSERT_TRUE(
JS_GetProperty(aCx, extraObj, extraKeyFiledetails.get(), &extraValF))
<< "Must be able to get the extra key's value for filedetails";
ASSERT_TRUE(jsStr.init(aCx, extraValF))
<< "Extra value filedetails must be able to be init'd to a jsstring.";
ASSERT_STREQ(NS_ConvertUTF16toUTF8(jsStr).get(),
currentTest.expected.extraValueFiledetails.get())
<< "Reported value for extra filedetails should equals supplied value";
}
}
|