Bug 994134 - Warn first time users on pasting code into the console. r=bgrins

--HG--
rename : browser/devtools/webconsole/test/browser_webconsole_bug_642615_autocomplete.js => browser/devtools/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js
This commit is contained in:
Manish Goregaokar 2014-05-22 00:34:00 +02:00
parent 41a099d157
commit de4be8677c
9 changed files with 183 additions and 21 deletions

View File

@ -1396,6 +1396,10 @@ pref("devtools.browserconsole.filter.secwarn", true);
// Text size in the Web Console. Use 0 for the system default size. // Text size in the Web Console. Use 0 for the system default size.
pref("devtools.webconsole.fontSize", 0); pref("devtools.webconsole.fontSize", 0);
// Number of usages of the web console or scratchpad.
// If this is less than 10, then pasting code into the web console or scratchpad is disabled
pref("devtools.selfxss.count", 0);
// Persistent logging: |true| if you want the Web Console to keep all of the // Persistent logging: |true| if you want the Web Console to keep all of the
// logged messages after reloading the page, |false| if you want the output to // logged messages after reloading the page, |false| if you want the output to
// be cleared each time page navigation happens. // be cleared each time page navigation happens.

View File

@ -69,9 +69,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "EnvironmentClient",
XPCOMUtils.defineLazyModuleGetter(this, "ObjectClient", XPCOMUtils.defineLazyModuleGetter(this, "ObjectClient",
"resource://gre/modules/devtools/dbg-client.jsm"); "resource://gre/modules/devtools/dbg-client.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer", XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
"resource://gre/modules/devtools/dbg-server.jsm"); "resource://gre/modules/devtools/dbg-server.jsm");
@ -92,6 +89,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Reflect",
let telemetry = new Telemetry(); let telemetry = new Telemetry();
telemetry.toolOpened("scratchpad"); telemetry.toolOpened("scratchpad");
let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
/** /**
* The scratchpad object handles the Scratchpad window functionality. * The scratchpad object handles the Scratchpad window functionality.
*/ */
@ -494,6 +493,7 @@ var Scratchpad = {
*/ */
execute: function SP_execute() execute: function SP_execute()
{ {
WebConsoleUtils.usageCount++;
let selection = this.editor.getSelection() || this.getText(); let selection = this.editor.getSelection() || this.getText();
return this.evaluate(selection); return this.evaluate(selection);
}, },
@ -1613,11 +1613,16 @@ var Scratchpad = {
}; };
this.editor = new Editor(config); this.editor = new Editor(config);
this.editor.appendTo(document.querySelector("#scratchpad-editor")).then(() => { let editorElement = document.querySelector("#scratchpad-editor");
this.editor.appendTo(editorElement).then(() => {
var lines = initialText.split("\n"); var lines = initialText.split("\n");
this.editor.setupAutoCompletion(); this.editor.setupAutoCompletion();
this.editor.on("change", this._onChanged); this.editor.on("change", this._onChanged);
this._onPaste = WebConsoleUtils.pasteHandlerGen(this.editor.container.contentDocument.body,
document.querySelector('#scratchpad-notificationbox'));
editorElement.addEventListener("paste", this._onPaste);
editorElement.addEventListener("drop", this._onPaste);
this.editor.on("save", () => this.saveFile()); this.editor.on("save", () => this.saveFile());
this.editor.focus(); this.editor.focus();
this.editor.setCursor({ line: lines.length, ch: lines.pop().length }); this.editor.setCursor({ line: lines.length, ch: lines.pop().length });
@ -1689,7 +1694,12 @@ var Scratchpad = {
PreferenceObserver.uninit(); PreferenceObserver.uninit();
CloseObserver.uninit(); CloseObserver.uninit();
if (this._onPaste) {
let editorElement = document.querySelector("#scratchpad-editor");
editorElement.removeEventListener("paste", this._onPaste);
editorElement.removeEventListener("drop", this._onPaste);
this._onPaste = null;
}
this.editor.off("change", this._onChanged); this.editor.off("change", this._onChanged);
this.editor.destroy(); this.editor.destroy();
this.editor = null; this.editor = null;

View File

@ -5,10 +5,11 @@
"use strict"; "use strict";
let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
function test() function test()
{ {
waitForExplicitFinish(); waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() { gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
@ -56,6 +57,21 @@ function runTests()
let anchor = doc.documentElement; let anchor = doc.documentElement;
let isContextMenu = false; let isContextMenu = false;
let oldVal = sp.editor.getText();
let testSelfXss = function (oldVal) {
// Self xss prevention tests (bug 994134)
info("Self xss paste tests")
is(WebConsoleUtils.usageCount, 0, "Test for usage count getter")
let notificationbox = doc.getElementById("scratchpad-notificationbox");
let notification = notificationbox.getNotificationWithValue('selfxss-notification');
ok(notification, "Self-xss notification shown");
is(oldVal, sp.editor.getText(), "Paste blocked by self-xss prevention");
Services.prefs.setIntPref("devtools.selfxss.count", 10);
notificationbox.removeAllNotifications(true);
openMenu(10, 10, firstShow);
};
let openMenu = function(aX, aY, aCallback) { let openMenu = function(aX, aY, aCallback) {
if (!editMenu || OS != "Darwin") { if (!editMenu || OS != "Darwin") {
menuPopup.addEventListener("popupshown", function onPopupShown() { menuPopup.addEventListener("popupshown", function onPopupShown() {
@ -137,8 +153,8 @@ function runTests()
}; };
let hideAfterCut = function() { let hideAfterCut = function() {
sp.editor.on("change", onPaste);
waitForFocus(function () { waitForFocus(function () {
sp.editor.on("change", onPaste);
EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow); EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
}, gScratchpadWindow); }, gScratchpadWindow);
}; };
@ -179,6 +195,9 @@ function runTests()
sp.setText("bug 699130: hello world! (context menu)"); sp.setText("bug 699130: hello world! (context menu)");
openMenu(10, 10, firstShow); openMenu(10, 10, firstShow);
}; };
waitForFocus(function(){
openMenu(10, 10, firstShow); WebConsoleUtils.usageCount = 0;
EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
testSelfXss(oldVal);
}, gScratchpadWindow)
} }

View File

@ -209,7 +209,7 @@ run-if = os == "win"
[browser_webconsole_bug_632347_iterators_generators.js] [browser_webconsole_bug_632347_iterators_generators.js]
[browser_webconsole_bug_632817.js] [browser_webconsole_bug_632817.js]
[browser_webconsole_bug_642108_pruneTest.js] [browser_webconsole_bug_642108_pruneTest.js]
[browser_webconsole_bug_642615_autocomplete.js] [browser_webconsole_autocomplete_and_selfxss.js]
[browser_webconsole_bug_644419_log_limits.js] [browser_webconsole_bug_644419_log_limits.js]
[browser_webconsole_bug_646025_console_file_location.js] [browser_webconsole_bug_646025_console_file_location.js]
[browser_webconsole_bug_651501_document_body_autocomplete.js] [browser_webconsole_bug_651501_document_body_autocomplete.js]

View File

@ -7,6 +7,7 @@ const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642615";
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper", XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "@mozilla.org/widget/clipboardhelper;1",
"nsIClipboardHelper"); "nsIClipboardHelper");
let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
function consoleOpened(HUD) { function consoleOpened(HUD) {
let jsterm = HUD.jsterm; let jsterm = HUD.jsterm;
@ -35,13 +36,48 @@ function consoleOpened(HUD) {
} }
function onClipboardCopy() { function onClipboardCopy() {
info("wait for completion update after clipboard paste"); testSelfXss();
jsterm.once("autocomplete-updated", onClipboardPaste);
jsterm.setInputValue("docu");
info("wait for completion update after clipboard paste");
updateEditUIVisibility(); updateEditUIVisibility();
jsterm.once("autocomplete-updated", onClipboardPaste);
goDoCommand("cmd_paste"); goDoCommand("cmd_paste");
} }
// Self xss prevention tests (bug 994134)
function testSelfXss(){
info("Self-xss paste tests")
WebConsoleUtils.usageCount = 0;
is(WebConsoleUtils.usageCount, 0, "Test for usage count getter")
// Input some commands to check if usage counting is working
for(let i = 0; i <= 5; i++){
jsterm.setInputValue(i);
jsterm.execute();
}
is(WebConsoleUtils.usageCount, 6, "Usage count incremented")
WebConsoleUtils.usageCount = 0;
updateEditUIVisibility();
let oldVal = jsterm.inputNode.value;
goDoCommand("cmd_paste");
let notificationbox = jsterm.hud.document.getElementById("webconsole-notificationbox");
let notification = notificationbox.getNotificationWithValue('selfxss-notification');
ok(notification, "Self-xss notification shown");
is(oldVal, jsterm.inputNode.value, "Paste blocked by self-xss prevention");
// Allow pasting
jsterm.inputNode.value = "allow pasting";
var evt = document.createEvent("KeyboardEvent");
evt.initKeyEvent ("keyup", true, true, window,
0, 0, 0, 0,
0, " ".charCodeAt(0));
jsterm.inputNode.dispatchEvent(evt);
jsterm.inputNode.value = "";
goDoCommand("cmd_paste");
isnot("", jsterm.inputNode.value, "Paste works");
}
function onClipboardPaste() { function onClipboardPaste() {
ok(!jsterm.completeNode.value, "no completion value after paste"); ok(!jsterm.completeNode.value, "no completion value after paste");

View File

@ -3133,7 +3133,10 @@ JSTerm.prototype = {
inputContainer.style.display = "none"; inputContainer.style.display = "none";
} }
else { else {
this._onPaste = WebConsoleUtils.pasteHandlerGen(this.inputNode, doc.getElementById("webconsole-notificationbox"));
this.inputNode.addEventListener("keypress", this._keyPress, false); this.inputNode.addEventListener("keypress", this._keyPress, false);
this.inputNode.addEventListener("paste", this._onPaste);
this.inputNode.addEventListener("drop", this._onPaste);
this.inputNode.addEventListener("input", this._inputEventHandler, false); this.inputNode.addEventListener("input", this._inputEventHandler, false);
this.inputNode.addEventListener("keyup", this._inputEventHandler, false); this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
this.inputNode.addEventListener("focus", this._focusEventHandler, false); this.inputNode.addEventListener("focus", this._focusEventHandler, false);
@ -3272,6 +3275,7 @@ JSTerm.prototype = {
// value that was not evaluated yet. // value that was not evaluated yet.
this.history[this.historyIndex++] = aExecuteString; this.history[this.historyIndex++] = aExecuteString;
this.historyPlaceHolder = this.history.length; this.historyPlaceHolder = this.history.length;
WebConsoleUtils.usageCount++;
this.setInputValue(""); this.setInputValue("");
this.clearCompletion(); this.clearCompletion();
}, },
@ -4524,6 +4528,12 @@ JSTerm.prototype = {
popup.parentNode.removeChild(popup); popup.parentNode.removeChild(popup);
} }
if (this._onPaste) {
this.inputNode.removeEventListener("paste", this._onPaste, false);
this.inputNode.removeEventListener("drop", this._onPaste, false);
this._onPaste = null;
}
this.inputNode.removeEventListener("keypress", this._keyPress, false); this.inputNode.removeEventListener("keypress", this._keyPress, false);
this.inputNode.removeEventListener("input", this._inputEventHandler, false); this.inputNode.removeEventListener("input", this._inputEventHandler, false);
this.inputNode.removeEventListener("keyup", this._inputEventHandler, false); this.inputNode.removeEventListener("keyup", this._inputEventHandler, false);

View File

@ -173,15 +173,16 @@ function goUpdateConsoleCommands() {
<hbox id="output-wrapper" flex="1" context="output-contextmenu" tooltip="aHTMLTooltip"> <hbox id="output-wrapper" flex="1" context="output-contextmenu" tooltip="aHTMLTooltip">
<div xmlns="http://www.w3.org/1999/xhtml" id="output-container" tabindex="1" /> <div xmlns="http://www.w3.org/1999/xhtml" id="output-container" tabindex="1" />
</hbox> </hbox>
<notificationbox id="webconsole-notificationbox">
<hbox class="jsterm-input-container" style="direction:ltr"> <hbox class="jsterm-input-container" style="direction:ltr">
<stack class="jsterm-stack-node" flex="1"> <stack class="jsterm-stack-node" flex="1">
<textbox class="jsterm-complete-node devtools-monospace" <textbox class="jsterm-complete-node devtools-monospace"
multiline="true" rows="1" tabindex="-1"/> multiline="true" rows="1" tabindex="-1"/>
<textbox class="jsterm-input-node devtools-monospace" <textbox class="jsterm-input-node devtools-monospace"
multiline="true" rows="1" tabindex="0"/> multiline="true" rows="1" tabindex="0"/>
</stack> </stack>
</hbox> </hbox>
</notificationbox>
</vbox> </vbox>
<splitter class="devtools-side-splitter"/> <splitter class="devtools-side-splitter"/>

View File

@ -223,6 +223,16 @@ openNodeInInspector=Click to select the node in the inspector
# cd() is invoked with an invalid argument. # cd() is invoked with an invalid argument.
cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument. cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument.
# LOCALIZATION NOTE (selfxss.msg): the text that is displayed when
# a new user of the developer tools pastes code into the console
# %1 is the text of selfxss.okstring
selfxss.msg=Scam Warning: Take care when pasting things you don't understand. This could allow attackers to steal your identity or take control of your computer. Please type '%S' below (no need to press enter) to allow pasting.
# LOCALIZATION NOTE (selfxss.msg): the string to be typed
# in by a new user of the developer tools when they receive the sefxss.msg prompt.
# Please avoid using non-keyboard characters here
selfxss.okstring=allow pasting
# LOCALIZATION NOTE (messageToggleDetails): the text that is displayed when # LOCALIZATION NOTE (messageToggleDetails): the text that is displayed when
# you hover the arrow for expanding/collapsing the message details. For # you hover the arrow for expanding/collapsing the message details. For
# console.error() and other messages we show the stacktrace. # console.error() and other messages we show the stacktrace.

View File

@ -31,6 +31,8 @@ const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
// Match the function arguments from the result of toString() or toSource(). // Match the function arguments from the result of toString() or toSource().
const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/; const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/;
// Number of terminal entries for the self-xss prevention to go away
const CONSOLE_ENTRY_THRESHOLD = 10
let WebConsoleUtils = { let WebConsoleUtils = {
/** /**
* Convenience function to unwrap a wrapped object. * Convenience function to unwrap a wrapped object.
@ -530,7 +532,77 @@ let WebConsoleUtils = {
{ {
return aGrip && typeof(aGrip) == "object" && aGrip.actor; return aGrip && typeof(aGrip) == "object" && aGrip.actor;
}, },
/**
* Value of devtools.selfxss.count preference
*
* @type number
* @private
*/
_usageCount: 0,
get usageCount() {
if (WebConsoleUtils._usageCount < CONSOLE_ENTRY_THRESHOLD) {
WebConsoleUtils._usageCount = Services.prefs.getIntPref("devtools.selfxss.count")
}
return WebConsoleUtils._usageCount;
},
set usageCount(newUC) {
if (newUC <= CONSOLE_ENTRY_THRESHOLD) {
WebConsoleUtils._usageCount = newUC;
Services.prefs.setIntPref("devtools.selfxss.count", newUC);
}
},
/**
* The inputNode "paste" event handler generator. Helps prevent self-xss attacks
*
* @param nsIDOMElement inputField
* @param nsIDOMElement notificationBox
* @returns A function to be added as a handler to 'paste' and 'drop' events on the input field
*/
pasteHandlerGen: function WCU_pasteHandlerGen(inputField, notificationBox){
let handler = function WCU_pasteHandler(aEvent) {
if (WebConsoleUtils.usageCount >= CONSOLE_ENTRY_THRESHOLD) {
inputField.removeEventListener("paste", handler);
inputField.removeEventListener("drop", handler);
return true;
}
if (notificationBox.getNotificationWithValue("selfxss-notification")) {
aEvent.preventDefault();
aEvent.stopPropagation();
return false;
}
let l10n = new WebConsoleUtils.l10n("chrome://browser/locale/devtools/webconsole.properties");
let okstring = l10n.getStr("selfxss.okstring");
let msg = l10n.getFormatStr("selfxss.msg", [okstring]);
let notification = notificationBox.appendNotification(msg,
"selfxss-notification", null, notificationBox.PRIORITY_WARNING_HIGH, null,
function(eventType) {
// Cleanup function if notification is dismissed
if (eventType == "removed") {
inputField.removeEventListener("keyup", pasteKeyUpHandler);
}
});
function pasteKeyUpHandler(aEvent2) {
let value = inputField.value || inputField.textContent;
if (value.contains(okstring)) {
notificationBox.removeNotification(notification);
inputField.removeEventListener("keyup", pasteKeyUpHandler);
WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
}
}
inputField.addEventListener("keyup", pasteKeyUpHandler);
aEvent.preventDefault();
aEvent.stopPropagation();
return false;
};
return handler;
},
}; };
exports.Utils = WebConsoleUtils; exports.Utils = WebConsoleUtils;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////