Bug 1359350: Add no-eval rule to eslint config (and whitelist failures in tests) r?Standard8 draft
authorFrederik Braun <fbraun@mozilla.com>
Mon, 08 May 2017 14:50:50 +0200
changeset 574269 9b010e6a497016cce08ec0cccb31531bcd82ddfc
parent 574065 1fda52a1f3b81cf1a821155998dca637bb64e3d9
child 627532 37da26aa3e671f25c887dc6a7a72a898da6ddcd2
push id57639
push userbmo:fbraun@mozilla.com
push dateMon, 08 May 2017 15:55:28 +0000
reviewersStandard8
bugs1359350
milestone55.0a1
Bug 1359350: Add no-eval rule to eslint config (and whitelist failures in tests) r?Standard8 MozReview-Commit-ID: 4nYlX4sSdbF
.eslintrc.js
browser/base/content/test/general/browser_aboutHome.js
browser/base/content/test/general/browser_bug633691.js
browser/base/content/test/newtab/head.js
browser/components/preferences/in-content-old/tests/browser_subdialogs.js
browser/components/preferences/in-content/tests/browser_subdialogs.js
browser/components/sessionstore/test/browser_async_remove_tab.js
dom/indexedDB/test/test_error_events_abort_transactions.html
dom/indexedDB/test/test_event_propagation.html
dom/indexedDB/test/test_exceptions_in_events.html
dom/indexedDB/test/test_third_party.html
dom/indexedDB/test/third_party_iframe1.html
dom/indexedDB/test/unit/test_complex_keyPaths.js
services/sync/tests/unit/test_utils_deepEquals.js
toolkit/components/contentprefs/tests/unit_cps2/test_observers.js
toolkit/components/ctypes/tests/unit/test_jsctypes.js
toolkit/components/feeds/test/test_xml.js
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_password_field_autocomplete.html
toolkit/modules/tests/browser/browser_FinderHighlighter.js
toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
toolkit/modules/tests/xpcshell/test_Log.js
toolkit/mozapps/update/content/updates.js
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
tools/lint/eslint/eslint-plugin-mozilla/package.json
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -10,21 +10,21 @@ module.exports = {
     "mozilla/avoid-nsISupportsString-preferences": "error",
     "mozilla/import-browser-window-globals": "error",
     "mozilla/import-globals": "warn",
     "mozilla/no-import-into-var-and-global": "error",
     "mozilla/no-useless-parameters": "error",
     "mozilla/no-useless-removeEventListener": "error",
     "mozilla/use-default-preference-values": "error",
     "mozilla/use-ownerGlobal": "error",
-
     // No (!foo in bar) or (!object instanceof Class)
     "no-unsafe-negation": "error",
     // No eval() and no strings in the first param of setTimeout or setInterval
     "no-implied-eval": "error",
+    "no-eval": "error",
   },
   "env": {
     "es6": true
   },
   "parserOptions": {
     "ecmaVersion": 8,
   },
 };
--- a/browser/base/content/test/general/browser_aboutHome.js
+++ b/browser/base/content/test/general/browser_aboutHome.js
@@ -563,16 +563,17 @@ function* withSnippetsMap(setupFn, testF
       }, function* (args) {
         return new Promise(resolve => {
           let document = content.document;
           // We're not using Promise-based listeners, because they resolve asynchronously.
           // The snippets test setup code relies on synchronous behaviour here.
           document.addEventListener("AboutHomeLoadSnippets", function() {
             let updateSnippets;
             if (args.setupFnSource) {
+              // eslint-disable-next-line no-eval
               updateSnippets = eval(`(() => (${args.setupFnSource}))()`);
             }
 
             content.wrappedJSObject.ensureSnippetsMapThen(snippetsMap => {
               snippetsMap = Cu.waiveXrays(snippetsMap);
               info("Got snippets map: " +
                    "{ last-update: " + snippetsMap.get("snippets-last-update") +
                    ", cached-version: " + snippetsMap.get("snippets-cached-version") +
--- a/browser/base/content/test/general/browser_bug633691.js
+++ b/browser/base/content/test/general/browser_bug633691.js
@@ -11,18 +11,20 @@ add_task(function* test() {
     function* ({ is_element_hidden_, is_hidden_ }) {
       let loadError =
         ContentTaskUtils.waitForEvent(this, "AboutNetErrorLoad", false, null, true);
       let iframe = content.document.querySelector("iframe");
       iframe.src = "https://expired.example.com/";
 
       yield loadError;
 
+      /* eslint-disable no-eval */
       let is_hidden = eval(`(() => ${is_hidden_})()`);
       let is_element_hidden = eval(`(() => ${is_element_hidden_})()`);
+      /* eslint-enable no-eval */
       let doc = content.document.getElementsByTagName("iframe")[0].contentDocument;
       let aP = doc.getElementById("badCertAdvancedPanel");
       ok(aP, "Advanced content should exist");
       void is_hidden; // Quiet eslint warnings (actual use under is_element_hidden)
       is_element_hidden(aP, "Advanced content should not be visible by default")
     });
   });
 });
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -138,16 +138,17 @@ add_task(function* setup() {
   * @param aIndex index of cell
   * @param aFn function to call in child process or tab.
   * @returns result of calling the function.
   */
 function performOnCell(aIndex, aFn) {
   return ContentTask.spawn(gWindow.gBrowser.selectedBrowser,
                            { index: aIndex, fn: aFn.toString() }, function* (args) {
     let cell = content.gGrid.cells[args.index];
+    // eslint-disable-next-line no-eval
     return eval(args.fn)(cell);
   });
 }
 
 /**
  * Allows to provide a list of links that is used to construct the grid.
  * @param aLinksPattern the pattern (see below)
  *
--- a/browser/components/preferences/in-content-old/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content-old/tests/browser_subdialogs.js
@@ -19,16 +19,17 @@ function* open_subdialog_and_test_generi
     let win = content.window;
     let subdialog = win.gSubDialog;
     subdialog.open(args.url, null, rv);
 
     info("waiting for subdialog DOMFrameContentLoaded");
     yield ContentTaskUtils.waitForEvent(win, "DOMFrameContentLoaded", true);
     let result;
     if (args.domcontentloadedFnStr) {
+      // eslint-disable-next-line no-eval
       result = eval(args.domcontentloadedFnStr);
     }
 
     info("waiting for subdialog load");
     yield ContentTaskUtils.waitForEvent(subdialog._frame, "load");
     info("subdialog window is loaded");
 
     let expectedStyleSheetURLs = subdialog._injectedStyleSheets.slice(0);
--- a/browser/components/preferences/in-content/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content/tests/browser_subdialogs.js
@@ -19,16 +19,17 @@ function* open_subdialog_and_test_generi
     let win = content.window;
     let subdialog = win.gSubDialog;
     subdialog.open(args.url, null, rv);
 
     info("waiting for subdialog DOMFrameContentLoaded");
     yield ContentTaskUtils.waitForEvent(win, "DOMFrameContentLoaded", true);
     let result;
     if (args.domcontentloadedFnStr) {
+      // eslint-disable-next-line no-eval
       result = eval(args.domcontentloadedFnStr);
     }
 
     info("waiting for subdialog load");
     yield ContentTaskUtils.waitForEvent(subdialog._frame, "load");
     info("subdialog window is loaded");
 
     let expectedStyleSheetURLs = subdialog._injectedStyleSheets.slice(0);
--- a/browser/components/sessionstore/test/browser_async_remove_tab.js
+++ b/browser/components/sessionstore/test/browser_async_remove_tab.js
@@ -33,16 +33,17 @@ function restoreClosedTabWithValue(rval)
 }
 
 function promiseNewLocationAndHistoryEntryReplaced(browser, snippet) {
   return ContentTask.spawn(browser, snippet, function* (codeSnippet) {
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let shistory = webNavigation.sessionHistory;
 
     // Evaluate the snippet that the changes the location.
+    // eslint-disable-next-line no-eval
     eval(codeSnippet);
 
     return new Promise(resolve => {
       let listener = {
         OnHistoryReplaceEntry() {
           shistory.removeSHistoryListener(this);
           resolve();
         },
--- a/dom/indexedDB/test/test_error_events_abort_transactions.html
+++ b/dom/indexedDB/test/test_error_events_abort_transactions.html
@@ -9,16 +9,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript">
     function runTest() {
       SimpleTest.waitForExplicitFinish();
 
       function messageListener(event) {
+        // eslint-disable-next-line no-eval
         eval(event.data);
       }
 
       window.addEventListener("message", messageListener);
     }
   </script>
 
 </head>
--- a/dom/indexedDB/test/test_event_propagation.html
+++ b/dom/indexedDB/test/test_event_propagation.html
@@ -9,16 +9,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript">
     function runTest() {
       SimpleTest.waitForExplicitFinish();
 
       function messageListener(event) {
+        // eslint-disable-next-line no-eval
         eval(event.data);
       }
 
       window.addEventListener("message", messageListener);
     }
   </script>
 
 </head>
--- a/dom/indexedDB/test/test_exceptions_in_events.html
+++ b/dom/indexedDB/test/test_exceptions_in_events.html
@@ -9,16 +9,17 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript">
     function runTest() {
       SimpleTest.waitForExplicitFinish();
 
       function messageListener(event) {
+        // eslint-disable-next-line no-eval
         eval(event.data);
       }
 
       window.addEventListener("message", messageListener);
     }
   </script>
 
 </head>
--- a/dom/indexedDB/test/test_third_party.html
+++ b/dom/indexedDB/test/test_third_party.html
@@ -65,16 +65,17 @@
         'set': [["network.cookie.cookieBehavior", testData[testIndex].cookieBehavior]]
       }, () => {
         iframe.src = testData[testIndex].host + iframe1Path;
       });
       // SpecialPowers.setIntPref("network.cookie.cookieBehavior", testData[testIndex].cookieBehavior);
     }
 
     function messageListener(event) {
+      // eslint-disable-next-line no-eval
       let message = eval(event.data);
 
       is(message.source, "iframe", "Good source");
       is(message.result, testData[testIndex].expectedResult, "Good result");
 
       if (testIndex < testData.length - 1) {
         testIndex++;
         setiframe();
--- a/dom/indexedDB/test/third_party_iframe1.html
+++ b/dom/indexedDB/test/third_party_iframe1.html
@@ -3,16 +3,17 @@
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html>
 <head>
   <title>Indexed Database Test</title>
 
   <script type="text/javascript">
     function messageListener(event) {
+      // eslint-disable-next-line no-eval
       let message = eval(event.data);
 
       if (message.source == "parent") {
         document.getElementById("iframe2").src = message.href;
       }
       else if (message.source == "iframe") {
         parent.postMessage(event.data, "*");
       }
--- a/dom/indexedDB/test/unit/test_complex_keyPaths.js
+++ b/dom/indexedDB/test/unit/test_complex_keyPaths.js
@@ -134,16 +134,17 @@ function* testSteps()
     request.onsuccess = grabEventAndContinueHandler;
     yield undefined;
     ok(true, "Successfully updated cursor" + test);
 
     // Check that cursor.update throws as expected when key is changed
     let newValue = cursor.value;
     let destProp = Array.isArray(info.keyPath) ? info.keyPath[0] : info.keyPath;
     if (destProp) {
+      // eslint-disable-next-line no-eval
       eval("newValue." + destProp + " = 'newKeyValue'");
     }
     else {
       newValue = 'newKeyValue';
     }
     let didThrow;
     try {
       cursor.update(newValue);
--- a/services/sync/tests/unit/test_utils_deepEquals.js
+++ b/services/sync/tests/unit/test_utils_deepEquals.js
@@ -1,16 +1,18 @@
 _("Make sure Utils.deepEquals correctly finds items that are deeply equal");
 Cu.import("resource://services-sync/util.js");
 
 function run_test() {
   let data = '[NaN, undefined, null, true, false, Infinity, 0, 1, "a", "b", {a: 1}, {a: "a"}, [{a: 1}], [{a: true}], {a: 1, b: 2}, [1, 2], [1, 2, 3]]';
   _("Generating two copies of data:", data);
+  /* eslint-disable no-eval */
   let d1 = eval(data);
   let d2 = eval(data);
+  /* eslint-enable no-eval */
 
   d1.forEach(function(a) {
     _("Testing", a, typeof a, JSON.stringify([a]));
     let numMatch = 0;
 
     d2.forEach(function(b) {
       if (Utils.deepEquals(a, b)) {
         numMatch++;
--- a/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js
+++ b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js
@@ -4,18 +4,20 @@
 
 let global = this;
 
 function run_test() {
   var allTests = [];
   for (var i = 0; i < tests.length; i++) {
     // Generate two wrappers of each test function that invoke the original test with an
     // appropriate privacy context.
+    /* eslint-disable no-eval */
     var pub = eval("var f = function* " + tests[i].name + "() { yield tests[" + i + "](privateLoadContext); }; f");
     var priv = eval("var f = function* " + tests[i].name + "_private() { yield tests[" + i + "](privateLoadContext); }; f");
+    /* eslint-enable no-eval */
     allTests.push(pub);
     allTests.push(priv);
   }
   allTests = allTests.concat(specialTests);
   runAsyncTests(allTests);
 }
 
 var tests = [
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -944,16 +944,17 @@ function run_float_tests(library, t, nam
               {toString() { return 7; }},
               {valueOf() { return 7; }}];
   for (let i = 0; i < vals.length; i++)
     do_check_throws(function() { d.value = vals[i]; }, TypeError);
 
   // Check that values roundtrip through toSource() correctly.
   function test_roundtrip(tFn, val) {
     let f1 = tFn(val);
+    // eslint-disable-next-line no-eval
     var f2 = eval(f1.toSource());
     do_check_eq(f1.value, f2.value);
   }
   vals = [Infinity, -Infinity, -0, 0, 1, -1, 1 / 3, -1 / 3, 1 / 4, -1 / 4,
           1e-14, -1e-14, 0xfffffffffffff000, -0xfffffffffffff000];
   for (let i = 0; i < vals.length; i++)
     test_roundtrip(t, vals[i]);
   do_check_eq(t(NaN).toSource(), t.toSource() + "(NaN)");
@@ -1538,16 +1539,17 @@ function run_StructType_tests() {
   do_check_true(g_a.constructor === g_t.ptr);
   do_check_eq(g_a.contents.a, s.b.a);
   do_check_throws(function() { s.addressOfField(); }, TypeError);
   do_check_throws(function() { s.addressOfField("d"); }, TypeError);
   do_check_throws(function() { s.addressOfField("a", 2); }, TypeError);
 
   do_check_eq(s.toSource(), "s_t(4, {\"a\": 7, \"b\": 2}, 10)");
   do_check_eq(s.toSource(), s.toString());
+  // eslint-disable-next-line no-eval
   var s2 = eval(s.toSource());
   do_check_true(s2.constructor === s_t);
   do_check_eq(s.b.b, s2.b.b);
 
   // Test that structs can be set from an object using 'value'.
   do_check_throws(function() { s.value; }, TypeError);
   let s_init = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13 };
   s.value = s_init;
@@ -2036,16 +2038,17 @@ function run_ArrayType_tests() {
   do_check_eq(ptrValue(b.addressOfElement(0)), ptrValue(p));
 
   // Test that arrays can be constructed through ImplicitConvert.
   let c_t = ctypes.int32_t.array(6);
   let c = c_t();
   c.value = [1, 2, 3, 4, 5, 6];
   do_check_eq(c.toSource(), "ctypes.int32_t.array(6)([1, 2, 3, 4, 5, 6])");
   do_check_eq(c.toSource(), c.toString());
+  // eslint-disable-next-line no-eval
   var c2 = eval(c.toSource());
   do_check_eq(c2.constructor.name, "int32_t[6]");
   do_check_eq(c2.length, 6);
   do_check_eq(c2[3], c[3]);
 
   c.value = c;
   do_check_eq(c[3], 4);
   do_check_throws(function() { c.value; }, TypeError);
--- a/toolkit/components/feeds/test/test_xml.js
+++ b/toolkit/components/feeds/test/test_xml.js
@@ -33,16 +33,17 @@ function FeedListener(testcase) {
 
 FeedListener.prototype = {
   handleResult(result) {
     var feed = result.doc;
     try {
       do_print("Testing feed " + this.testcase.file.path);
       Assert.ok(isIID(feed, Ci.nsIFeed), "Has feed interface");
 
+      // eslint-disable-next-line no-eval
       if (!eval(this.testcase.expect)) {
         Assert.ok(false, "expect failed for " + this.testcase.desc);
       } else {
         Assert.ok(true, "expect passed for " + this.testcase.desc);
       }
     } catch (e) {
       Assert.ok(false, "expect failed for " + this.testcase.desc + " ---- " + e.message);
     }
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -79,21 +79,23 @@ var setupScript = runInParent(function s
     Services.logins.addLogin(login8B);
     // login8C is added later
     Services.logins.addLogin(login10);
   } catch (e) {
     assert.ok(false, "addLogin threw: " + e);
   }
 
   addMessageListener("addLogin", loginVariableName => {
+    // eslint-disable-next-line no-eval
     let login = eval(loginVariableName);
     assert.ok(!!login, "Login to add is defined: " + loginVariableName);
     Services.logins.addLogin(login);
   });
   addMessageListener("removeLogin", loginVariableName => {
+    // eslint-disable-next-line no-eval
     let login = eval(loginVariableName);
     assert.ok(!!login, "Login to delete is defined: " + loginVariableName);
     Services.logins.removeLogin(login);
   });
 });
 </script>
 <p id="display"></p>
 
--- a/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
@@ -78,21 +78,23 @@ var setupScript = runInParent(function s
     Services.logins.addLogin(login8B);
     // login8C is added later
     Services.logins.addLogin(login10);
   } catch (e) {
     assert.ok(false, "addLogin threw: " + e);
   }
 
   addMessageListener("addLogin", loginVariableName => {
+    // eslint-disable-next-line no-eval
     let login = eval(loginVariableName);
     assert.ok(!!login, "Login to add is defined: " + loginVariableName);
     Services.logins.addLogin(login);
   });
   addMessageListener("removeLogin", loginVariableName => {
+    // eslint-disable-next-line no-eval
     let login = eval(loginVariableName);
     assert.ok(!!login, "Login to delete is defined: " + loginVariableName);
     Services.logins.removeLogin(login);
   });
 });
 </script>
 <p id="display"></p>
 
--- a/toolkit/components/passwordmgr/test/mochitest/test_password_field_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_password_field_autocomplete.html
@@ -49,21 +49,23 @@ var setupScript = runInParent(function s
     Services.logins.addLogin(login2);
     Services.logins.addLogin(login3);
     Services.logins.addLogin(login4);
   } catch (e) {
     assert.ok(false, "addLogin threw: " + e);
   }
 
   addMessageListener("addLogin", loginVariableName => {
+    // eslint-disable-next-line no-eval
     let login = eval(loginVariableName);
     assert.ok(!!login, "Login to add is defined: " + loginVariableName);
     Services.logins.addLogin(login);
   });
   addMessageListener("removeLogin", loginVariableName => {
+    // eslint-disable-next-line no-eval
     let login = eval(loginVariableName);
     assert.ok(!!login, "Login to delete is defined: " + loginVariableName);
     Services.logins.removeLogin(login);
   });
 });
 </script>
 <p id="display"></p>
 
--- a/toolkit/modules/tests/browser/browser_FinderHighlighter.js
+++ b/toolkit/modules/tests/browser/browser_FinderHighlighter.js
@@ -111,16 +111,17 @@ function promiseTestHighlighterOutput(br
         if ("animationCalls" in expectedResult) {
           Assert.greaterOrEqual(callCounts.animationCalls.length,
             expectedResult.animationCalls[0], `Min. animation calls should match for '${word}'.`);
           Assert.lessOrEqual(callCounts.animationCalls.length,
             expectedResult.animationCalls[1], `Max. animation calls should match for '${word}'.`);
         }
 
         // Allow more specific assertions to be tested in `extraTest`.
+        // eslint-disable-next-line no-eval
         extraTest = eval(extraTest);
         extraTest(lastMaskNode, lastOutlineNode, rects);
 
         resolve();
       }
 
       function stubAnonymousContentNode(domNode, anonNode) {
         let originals = [anonNode.setTextContentForElement,
--- a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
+++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
@@ -717,16 +717,17 @@ xhr.prototype = {
         };
         this[k](e);
       } else {
         do_print("Notifying " + item + ", but there are no listeners");
       }
     }
   },
   addEventListener(aEvent, aValue, aCapturing) {
+    // eslint-disable-next-line no-eval
     eval("this._on" + aEvent + " = aValue");
   },
   flags: Ci.nsIClassInfo.SINGLETON,
   getScriptableHelper: () => null,
   getInterfaces(aCount) {
     let interfaces = [Ci.nsISupports];
     aCount.value = interfaces.length;
     return interfaces;
--- a/toolkit/modules/tests/xpcshell/test_Log.js
+++ b/toolkit/modules/tests/xpcshell/test_Log.js
@@ -497,16 +497,17 @@ add_task(function* test_log_err_only() {
 
   /*
    * Check that log.error(err) is treated the same as
    * log.error(null, err) by the logMessage constructor; the formatMessage()
    * tests above ensure that the combination of null text and an error object
    * is formatted correctly.
    */
   try {
+    // eslint-disable-next-line no-eval
     eval("javascript syntax error");
   } catch (e) {
     log.error(e);
     let msg = appender.messages.pop();
     do_check_eq(msg.message, null);
     do_check_eq(msg.params, e);
   }
 });
@@ -571,16 +572,17 @@ add_task(function* format_errors() {
   let str = pFormat.format(err);
   do_check_true(str.includes("ReferenceError"));
   do_check_true(str.includes("ERROR_FILE:28"));
   do_check_true(str.includes("Ref Error"));
 
   // Test that JS-generated Errors are recognized and formatted.
   try {
     yield Promise.resolve();  // Scrambles the stack
+    // eslint-disable-next-line no-eval
     eval("javascript syntax error");
   } catch (e) {
     str = pFormat.format(e);
     do_check_true(str.includes("SyntaxError: missing ;"));
     // Make sure we identified it as an Error and formatted the error location as
     // lineNumber:columnNumber.
     do_check_true(str.includes(":1:11)"));
     // Make sure that we use human-readable stack traces
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -324,16 +324,17 @@ var gUpdates = {
     this.strings = document.getElementById("updateStrings");
     var brandStrings = document.getElementById("brandStrings");
     this.brandName = brandStrings.getString("brandShortName");
 
     var pages = this.wiz.childNodes;
     for (var i = 0; i < pages.length; ++i) {
       var page = pages[i];
       if (page.localName == "wizardpage")
+        // eslint-disable-next-line no-eval
         this._pages[page.pageid] = eval(page.getAttribute("object"));
     }
 
     // Cache the standard button labels in case we need to restore them
     this._cacheButtonStrings("next");
     this._cacheButtonStrings("finish");
     this._cacheButtonStrings("extra1");
     this._cacheButtonStrings("extra2");
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -114,16 +114,17 @@ module.exports = {
     // No duplicate cases in switch statements
     "no-duplicate-case": "error",
 
     // Disallow unnecessary calls to .bind()
     "no-extra-bind": "error",
 
     // Disallow eval and setInteral/setTimeout with strings
     "no-implied-eval": "error",
+    "no-eval": "error",
 
     // No labels
     "no-labels": "error",
 
     // Disallow unnecessary nested blocks
     "no-lone-blocks": "error",
 
     // If an if block ends with a return no need for an else block
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.2.46",
+  "version": "0.2.47",
   "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
   "keywords": [
     "eslint",
     "eslintplugin",
     "eslint-plugin",
     "mozilla",
     "firefox"
   ],