summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/xpcshell/rs-blocklist/test_blocklist_mlbf_stashes.js
blob: e129efc79382a858cc9d2b799da7f6da033caf34 (plain)
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* Any copyright is dedicated to the Public Domain.
 * https://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

Services.prefs.setBoolPref("extensions.blocklist.useMLBF", true);

createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");

const ExtensionBlocklistMLBF = getExtensionBlocklistMLBF();
const MLBF_LOAD_ATTEMPTS = [];
ExtensionBlocklistMLBF._fetchMLBF = async record => {
  MLBF_LOAD_ATTEMPTS.push(record);
  return {
    generationTime: 0,
    cascadeFilter: {
      has(blockKey) {
        if (blockKey === "@onlyblockedbymlbf:1") {
          return true;
        }
        throw new Error("bloom filter should not be used in this test");
      },
    },
  };
};

async function checkBlockState(addonId, version, expectBlocked) {
  let addon = {
    id: addonId,
    version,
    // Note: signedDate is missing, so the MLBF does not apply
    // and we will effectively only test stashing.
  };
  let state = await Blocklist.getAddonBlocklistState(addon);
  if (expectBlocked) {
    Assert.equal(state, Ci.nsIBlocklistService.STATE_BLOCKED);
  } else {
    Assert.equal(state, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  }
}

add_task(async function setup() {
  await promiseStartupManager();
});

// Tests that add-ons can be blocked / unblocked via the stash.
add_task(async function basic_stash() {
  await AddonTestUtils.loadBlocklistRawData({
    extensionsMLBF: [
      {
        stash_time: 0,
        stash: {
          blocked: ["@blocked:1"],
          unblocked: ["@notblocked:2"],
        },
      },
    ],
  });
  await checkBlockState("@blocked", "1", true);
  await checkBlockState("@notblocked", "2", false);
  // Not in stash (but unsigned, so shouldn't reach MLBF):
  await checkBlockState("@blocked", "2", false);

  Assert.equal(
    await Blocklist.getAddonBlocklistState({
      id: "@onlyblockedbymlbf",
      version: "1",
      signedDate: new Date(0), // = the MLBF's generationTime.
      signedState: AddonManager.SIGNEDSTATE_SIGNED,
    }),
    Ci.nsIBlocklistService.STATE_BLOCKED,
    "falls through to MLBF if entry is not found in stash"
  );

  Assert.deepEqual(MLBF_LOAD_ATTEMPTS, [null], "MLBF attachment not found");
});

// To complement the privileged_xpi_not_blocked in test_blocklist_mlbf.js,
// verify that privileged add-ons can still be blocked through stashes.
add_task(async function privileged_addon_blocked_by_stash() {
  const system_addon = {
    id: "@blocked",
    version: "1",
    signedDate: new Date(0), // = the MLBF's generationTime.
    signedState: AddonManager.SIGNEDSTATE_PRIVILEGED,
  };
  Assert.equal(
    await Blocklist.getAddonBlocklistState(system_addon),
    Ci.nsIBlocklistService.STATE_BLOCKED,
    "Privileged add-ons can still be blocked by a stash"
  );

  system_addon.signedState = AddonManager.SIGNEDSTATE_SYSTEM;
  Assert.equal(
    await Blocklist.getAddonBlocklistState(system_addon),
    Ci.nsIBlocklistService.STATE_BLOCKED,
    "Privileged system add-ons can still be blocked by a stash"
  );

  // For comparison, when an add-on is only blocked by a MLBF, the block
  // decision is ignored.
  system_addon.id = "@onlyblockedbymlbf";
  Assert.equal(
    await Blocklist.getAddonBlocklistState(system_addon),
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
    "Privileged add-ons cannot be blocked via a MLBF"
  );
  // (note that we haven't checked that SIGNEDSTATE_PRIVILEGED is not blocked
  // via the MLBF, but that is already covered by test_blocklist_mlbf.js ).
});

// To complement langpack_not_blocked_on_Nightly in test_blocklist_mlbf.js,
// verify that langpacks can still be blocked through stashes.
add_task(async function langpack_blocked_by_stash() {
  const langpack_addon = {
    id: "@blocked",
    type: "locale",
    version: "1",
    signedDate: new Date(0), // = the MLBF's generationTime.
    signedState: AddonManager.SIGNEDSTATE_SIGNED,
  };
  Assert.equal(
    await Blocklist.getAddonBlocklistState(langpack_addon),
    Ci.nsIBlocklistService.STATE_BLOCKED,
    "Langpack add-ons can still be blocked by a stash"
  );

  // For comparison, when an add-on is only blocked by a MLBF, the block
  // decision is ignored on Nightly (but blocked on non-Nightly).
  langpack_addon.id = "@onlyblockedbymlbf";
  if (AppConstants.NIGHTLY_BUILD) {
    Assert.equal(
      await Blocklist.getAddonBlocklistState(langpack_addon),
      Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
      "Langpack add-ons cannot be blocked via a MLBF on Nightly"
    );
  } else {
    Assert.equal(
      await Blocklist.getAddonBlocklistState(langpack_addon),
      Ci.nsIBlocklistService.STATE_BLOCKED,
      "Langpack add-ons can be blocked via a MLBF on non-Nightly"
    );
  }
});

// Tests that invalid stash entries are ignored.
add_task(async function invalid_stashes() {
  await AddonTestUtils.loadBlocklistRawData({
    extensionsMLBF: [
      {},
      { stash: null },
      { stash: 1 },
      { stash: {} },
      { stash: { blocked: ["@broken:1", "@okid:1"] } },
      { stash: { unblocked: ["@broken:2"] } },
      // The only correct entry:
      { stash: { blocked: ["@okid:2"], unblocked: ["@okid:1"] } },
      { stash: { blocked: ["@broken:1", "@okid:1"] } },
      { stash: { unblocked: ["@broken:2", "@okid:2"] } },
    ],
  });
  // The valid stash entry should be applied:
  await checkBlockState("@okid", "1", false);
  await checkBlockState("@okid", "2", true);
  // Entries from invalid stashes should be ignored:
  await checkBlockState("@broken", "1", false);
  await checkBlockState("@broken", "2", false);
});

// Blocklist stashes should be processed in the reverse chronological order.
add_task(async function stash_time_order() {
  await AddonTestUtils.loadBlocklistRawData({
    extensionsMLBF: [
      // "@a:1" and "@a:2" are blocked at time 1, but unblocked later.
      { stash_time: 2, stash: { blocked: [], unblocked: ["@a:1"] } },
      { stash_time: 1, stash: { blocked: ["@a:1", "@a:2"], unblocked: [] } },
      { stash_time: 3, stash: { blocked: [], unblocked: ["@a:2"] } },

      // "@b:1" and "@b:2" are unblocked at time 4, but blocked later.
      { stash_time: 5, stash: { blocked: ["@b:1"], unblocked: [] } },
      { stash_time: 4, stash: { blocked: [], unblocked: ["@b:1", "@b:2"] } },
      { stash_time: 6, stash: { blocked: ["@b:2"], unblocked: [] } },
    ],
  });
  await checkBlockState("@a", "1", false);
  await checkBlockState("@a", "2", false);

  await checkBlockState("@b", "1", true);
  await checkBlockState("@b", "2", true);
});

// Attachments with unsupported attachment_type should be ignored.
add_task(async function mlbf_bloomfilter_full_ignored() {
  MLBF_LOAD_ATTEMPTS.length = 0;

  await AddonTestUtils.loadBlocklistRawData({
    extensionsMLBF: [{ attachment_type: "bloomfilter-full", attachment: {} }],
  });

  // Only bloomfilter-base records should be used.
  // Since there are no such records, we shouldn't find anything.
  Assert.deepEqual(MLBF_LOAD_ATTEMPTS, [null], "no matching MLBFs found");
});

// Tests that the most recent MLBF is downloaded.
add_task(async function mlbf_generation_time_recent() {
  MLBF_LOAD_ATTEMPTS.length = 0;
  const records = [
    { attachment_type: "bloomfilter-base", attachment: {}, generation_time: 2 },
    { attachment_type: "bloomfilter-base", attachment: {}, generation_time: 3 },
    { attachment_type: "bloomfilter-base", attachment: {}, generation_time: 1 },
  ];
  await AddonTestUtils.loadBlocklistRawData({ extensionsMLBF: records });
  Assert.equal(
    MLBF_LOAD_ATTEMPTS[0].generation_time,
    3,
    "expected to load most recent MLBF"
  );
});