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
|
"use strict";
/* eslint-disable no-unused-vars */
// Loaded into the same scope as head_xpc.js
/* import-globals-from head_xpc.js */
const { Preferences } = ChromeUtils.import(
"resource://gre/modules/Preferences.jsm"
);
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
ChromeUtils.import("resource://normandy/lib/NormandyApi.jsm", this);
const CryptoHash = Components.Constructor(
"@mozilla.org/security/hash;1",
"nsICryptoHash",
"initWithString"
);
const FileInputStream = Components.Constructor(
"@mozilla.org/network/file-input-stream;1",
"nsIFileInputStream",
"init"
);
const preferenceBranches = {
user: Preferences,
default: new Preferences({ defaultBranch: true }),
};
// duplicated from test/browser/head.js until we move everything over to mochitests.
function withMockPreferences(testFunction) {
return async function inner(...args) {
const prefManager = new MockPreferences();
try {
await testFunction(...args, prefManager);
} finally {
prefManager.cleanup();
}
};
}
class MockPreferences {
constructor() {
this.oldValues = { user: {}, default: {} };
}
set(name, value, branch = "user") {
this.preserve(name, branch);
preferenceBranches[branch].set(name, value);
}
preserve(name, branch) {
if (!(name in this.oldValues[branch])) {
this.oldValues[branch][name] = preferenceBranches[branch].get(
name,
undefined
);
}
}
cleanup() {
for (const [branchName, values] of Object.entries(this.oldValues)) {
const preferenceBranch = preferenceBranches[branchName];
for (const [name, value] of Object.entries(values)) {
if (value !== undefined) {
preferenceBranch.set(name, value);
} else {
preferenceBranch.reset(name);
}
}
}
}
}
class MockResponse {
constructor(content) {
this.content = content;
}
async text() {
return this.content;
}
async json() {
return JSON.parse(this.content);
}
}
function withServer(server, task) {
return withMockPreferences(async function inner(preferences) {
const serverUrl = `http://localhost:${server.identity.primaryPort}`;
preferences.set("app.normandy.api_url", `${serverUrl}/api/v1`);
preferences.set(
"security.content.signature.root_hash",
// Hash of the key that signs the normandy dev certificates
"4C:35:B1:C3:E3:12:D9:55:E7:78:ED:D0:A7:E7:8A:38:83:04:EF:01:BF:FA:03:29:B2:46:9F:3C:C5:EC:36:04"
);
NormandyApi.clearIndexCache();
try {
await task(serverUrl, preferences, server);
} finally {
await new Promise(resolve => server.stop(resolve));
}
});
}
function makeScriptServer(scriptPath) {
const server = new HttpServer();
server.registerContentType("sjs", "sjs");
server.registerFile("/", do_get_file(scriptPath));
server.start(-1);
return server;
}
function withScriptServer(scriptPath, task) {
return withServer(makeScriptServer(scriptPath), task);
}
function makeMockApiServer(directory) {
const server = new HttpServer();
server.registerDirectory("/", directory);
server.setIndexHandler(async function(request, response) {
response.processAsync();
const dir = request.getProperty("directory");
const index = dir.clone();
index.append("index.json");
if (!index.exists()) {
response.setStatusLine("1.1", 404, "Not Found");
response.write(`Cannot find path ${index.path}`);
response.finish();
return;
}
try {
const contents = await OS.File.read(index.path, { encoding: "utf-8" });
response.write(contents);
} catch (e) {
response.setStatusLine("1.1", 500, "Server error");
response.write(e.toString());
} finally {
response.finish();
}
});
server.start(-1);
return server;
}
function withMockApiServer(task) {
return withServer(makeMockApiServer(do_get_file("mock_api")), task);
}
const CryptoUtils = {
_getHashStringForCrypto(aCrypto) {
// return the two-digit hexadecimal code for a byte
let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
// convert the binary hash data to a hex string.
let binary = aCrypto.finish(false);
let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
return hash.join("").toLowerCase();
},
/**
* Get the computed hash for a given file
* @param {nsIFile} file The file to be hashed
* @param {string} [algorithm] The hashing algorithm to use
*/
getFileHash(file, algorithm = "sha256") {
const crypto = CryptoHash(algorithm);
const fis = new FileInputStream(file, -1, -1, false);
crypto.updateFromStream(fis, file.fileSize);
const hash = this._getHashStringForCrypto(crypto);
fis.close();
return hash;
},
};
|