├── Images └── HowToInstall.gif ├── LICENSE ├── README.md └── extension ├── icon128.png ├── icon16.png ├── icon48.png ├── js ├── background.js ├── contentscript.js ├── menu.js └── web │ ├── accessibility │ ├── highlightElementsWithTheSameId.js │ ├── highlightImagesWithoutAltTags.js │ ├── highlightInputsWithoutLabel.js │ ├── removeElementsWithTheSameId.js │ ├── removeImagesWithoutAltTags.js │ ├── removeInputsWithoutLabel.js │ ├── removeStyleSheets.js │ └── visualiseTabFlow.js │ ├── exploits │ ├── sql.js │ └── xss.js │ ├── styling │ ├── increaseAllElementsText.js │ ├── increaseButtonsText.js │ ├── increaseLabelsText.js │ ├── increaseLinksText.js │ └── increasePagesText.js │ ├── utilities │ ├── decodeBase64ToConsole.js │ ├── documentDesignModeOff.js │ ├── documentDesignModeOn.js │ ├── encodeBase64ToConsole.js │ ├── forEveryDoThis.js │ ├── linkchecker.js │ └── prettyPrintJsonToConsole.js │ └── validation │ ├── changeInputTypesToText.js │ ├── removeMaxLength.js │ ├── removePasteRestrictions.js │ └── removeRequired.js └── manifest.json /Images/HowToInstall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eviltester/usefuljssnippetextension/acc84ade54a0722b0c83ec4d5b43b142bb20f750/Images/HowToInstall.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alan Richardson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![extension icon](https://github.com/eviltester/usefuljssnippetextension/blob/master/extension/icon48.png) Useful Snippets Extension 2 | 3 | A Chrome Extension with Useful JavaScript snippets for testing. 4 | 5 | Feel free to contribute to this set of snippets. 6 | 7 | What we are looking for are short JavaScript snippets that can run from a popup menu and provide small useful aids to support testing. 8 | 9 | The snippets should also be small enough that they provide useful examples of using JavaScript from the console or as Bookmarklets and help people learn to use JavaScript in their testing. 10 | 11 | ## Basic Install Instructions 12 | 13 | - Download and extract this repository somewhere 14 | - Navigate to `chrome://extensions` within Chrome 15 | - Ensure to switch 'Developer mode' on (a toggle at the top right of the page) 16 | - Select 'Load unpacked' and then navigate to the `/extension` folder 17 | - Navigate to a website and then right click it to access the `Useful Snippets` context menu 18 | - After executing a snippet you can see the bot code and get the code as a bookmarklet by viewing the DevTools console 19 | 20 | ![how to install extension](https://github.com/eviltester/usefuljssnippetextension/blob/master/Images/HowToInstall.gif) 21 | 22 | ## Current Snippets 23 | Below is a list of snippets currently available with this extension. Have an idea? Want to contribute? Please feel free to fork this repository, add snippets and then create a pull request. 24 | 25 | ### Accessibility 26 | * Remove images which do not have alt tags 27 | * Show images which do not have alt tags 28 | * Remove inputs which do not have matching `for` labels 29 | * Show inputs which do not have matching `for` labels 30 | * Remove elements with duplicate `id`'s 31 | * Show elements with duplicate `id`'s 32 | * Remove page style sheets 33 | * Visualise page tab flow 34 | 35 | ### Exploits 36 | * Insert JS Script Injection in all inputs 37 | * Insert SQL Injection in all inputs 38 | 39 | ### Styling 40 | * Check label text overflow 41 | * Check button text overflow 42 | * Check link text overflow 43 | * Check page text overflow 44 | * Check all elements text overflow 45 | 46 | ### Utilities 47 | * Switch document edit mode on 48 | * Switch document edit mode off 49 | * Pretty print a JSON string 50 | * Encode a string as Base64 51 | * Decode a Base64 string 52 | * For Every _element_ Do _this_ 53 | * will prompt twice, first for a CSS selector, next for some javascript to execute against the _element_ variable. 54 | * e.g. "p" and then "console.log(element.innerText)" would write out the text of all paragraphs to the console 55 | * e.g. "[role='checkbox']" and then "element.click()" would toggle all checkboxes 56 | * Link Checker 57 | * A simple link checker - all CORS and mixed content errors are displayed in console, console.table used for report output 58 | 59 | ### Validation (Client side) 60 | * Remove max length attributes from fields 61 | * Remove required field attributes from fields 62 | * Remove paste restrictions from fields 63 | * Change all inputs types to text 64 | 65 | ## Contributors 66 | 67 | See commit history for the authorship of the ongoing snippets. 68 | 69 | - [Alan Richardson](https://github.com/eviltester) created the basic framework for the Chrome Extension and contributed JavaScript snippets to this extension. 70 | - [eviltester.com](https://eviltester.com) 71 | - [@eviltester](https://twitter.com/eviltester) 72 | - [linkedin.com/in/eviltester](https://www.linkedin.com/in/eviltester) 73 | - [Viv Richards](https://github.com/vivrichards600) contributed JavaScript snippets to this extension. 74 | - [vivrichards.co.uk](http://vivrichards.co.uk/) 75 | - [@11vlr](https://twitter.com/11vlr) 76 | - [linkedin.com/in/vivrichards](https://www.linkedin.com/in/vivrichards) -------------------------------------------------------------------------------- /extension/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eviltester/usefuljssnippetextension/acc84ade54a0722b0c83ec4d5b43b142bb20f750/extension/icon128.png -------------------------------------------------------------------------------- /extension/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eviltester/usefuljssnippetextension/acc84ade54a0722b0c83ec4d5b43b142bb20f750/extension/icon16.png -------------------------------------------------------------------------------- /extension/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eviltester/usefuljssnippetextension/acc84ade54a0722b0c83ec4d5b43b142bb20f750/extension/icon48.png -------------------------------------------------------------------------------- /extension/js/background.js: -------------------------------------------------------------------------------- 1 | // 2 | // Setup the context menu 3 | // 4 | var contextMenus = {}; 5 | 6 | // the menuActions are defined in menu.js 7 | 8 | function findMenuRefItem(theMenuArray, theMenuRef){ 9 | for (var menuindex = 0; menuindex < theMenuArray.length; menuindex++) { 10 | if(theMenuArray[menuindex].menuref === theMenuRef){ 11 | return theMenuArray[menuindex].id; 12 | } 13 | } 14 | } 15 | 16 | for (var actionindex = 0; actionindex < menuActions.length; actionindex++) { 17 | if(menuActions[actionindex].menu===""){ 18 | // it is a root level menu, add it to the contextMenus 19 | menuActions[actionindex].id = chrome.contextMenus.create({ "title": menuActions[actionindex].title, "type": "normal", contexts: ["all"] }); 20 | }else{ 21 | // it is a child menu, find the parent and add it 22 | var parentId = findMenuRefItem(menuActions, menuActions[actionindex].menu); 23 | menuActions[actionindex].id = chrome.contextMenus.create({ "title": menuActions[actionindex].title, "type": "normal", contexts: ["all"], "parentId": parentId }); 24 | } 25 | } 26 | 27 | chrome.contextMenus.onClicked.addListener(contextMenuClickHandler); 28 | 29 | 30 | // 31 | // Handle a menu click 32 | // 33 | function contextMenuClickHandler(info, tab) { 34 | 35 | var actionToDo; 36 | 37 | // find the menuAction item for the menu clicked 38 | for (var actionindex = 0; actionindex < menuActions.length; actionindex++) { 39 | if (menuActions[actionindex].id === info.menuItemId) { 40 | actionToDo = menuActions[actionindex]; 41 | break; 42 | } 43 | } 44 | 45 | if(!actionToDo){ 46 | // could not find a menu action 47 | return; 48 | } 49 | 50 | if(actionToDo.file===""){ 51 | // no file associated with action 52 | return; 53 | } 54 | 55 | var errorHandler = function () { 56 | if (chrome.runtime.lastError) { 57 | console.error(chrome.runtime.lastError.message); 58 | } 59 | }; 60 | 61 | // file reading is async https://stackoverflow.com/questions/4100927/chrome-filereader 62 | function sendFileContentsAsMessage(filecontents) { 63 | chrome.tabs.sendMessage(tab.id, { type: "display", messageContents: "Script to Run:\n" }); 64 | chrome.tabs.sendMessage(tab.id, { type: "display", messageContents: filecontents }); 65 | } 66 | 67 | function sendFileContentsAsBookmarklet(filecontents) { 68 | var bookmarklet = "javascript:(function(){" + encodeURI(filecontents) + "})()"; 69 | chrome.tabs.sendMessage(tab.id, { type: "display", messageContents: "As Bookmarklet:\n" }); 70 | chrome.tabs.sendMessage(tab.id, { type: "display", messageContents: bookmarklet }); 71 | } 72 | 73 | 74 | // Execute the Action 75 | // cannot just execute script if the bot wants to access local variables 76 | // https://stackoverflow.com/questions/16784553/chrome-extension-throws-not-defined-on-defined-variable 77 | if (actionToDo.instant) { 78 | chrome.tabs.executeScript(null, { file: actionToDo.file }, errorHandler); 79 | } else { 80 | // inject execution script 81 | chrome.tabs.executeScript(tab.id, { file: 'js/contentscript.js' }, function () { 82 | // execute the snippet code by sending a message 83 | chrome.tabs.sendMessage(tab.id, { type: "execfile", filename: actionToDo.file }); 84 | }); 85 | } 86 | 87 | // Display the code for the action in the console 88 | getFileContents(actionToDo.file, errorHandler, sendFileContentsAsMessage); 89 | getFileContents(actionToDo.file, errorHandler, sendFileContentsAsBookmarklet); 90 | 91 | 92 | // read a file https://stackoverflow.com/questions/28858027/how-to-read-file-from-chrome-extension 93 | function getFileContents(filename, errorHandler, callback) { 94 | chrome.runtime.getPackageDirectoryEntry(function (root) { 95 | root.getFile(filename, {}, function (fileEntry) { 96 | fileEntry.file(function (file) { 97 | var reader = new FileReader(); 98 | reader.onloadend = function (e) { 99 | // contents are in .result 100 | callback(this.result); 101 | }; 102 | reader.readAsText(file); 103 | }, errorHandler); 104 | }, errorHandler); 105 | }); 106 | } 107 | } -------------------------------------------------------------------------------- /extension/js/contentscript.js: -------------------------------------------------------------------------------- 1 | // inject bot script 2 | if(window.haveInstalledBotListener!==true){ 3 | chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { 4 | if(message.type==="execfile"){ 5 | var script = document.createElement('script'); 6 | script.type = 'text/javascript'; 7 | script.src = chrome.extension.getURL(message.filename); 8 | document.getElementsByTagName('head')[0].appendChild(script); 9 | } 10 | if(message.type==="display"){ 11 | console.log(message.messageContents) 12 | } 13 | }); 14 | window.haveInstalledBotListener=true; 15 | } -------------------------------------------------------------------------------- /extension/js/menu.js: -------------------------------------------------------------------------------- 1 | // configure menus here to avoid writing menu creation code, 2 | // background.js will parse this to create the actual menus 3 | var menuActions = [ 4 | // parent menu items need to have a unique menuref, which is used by other items as the 'menu:' property 5 | // currently parents need to be defined first in the array 6 | { menu: "", "title": "Useful Snipppets", menuref: "W", file: ""}, 7 | { menu: "W", "title": "Accessibility", menuref: "W>A", file: ""}, 8 | { menu: "W>A", "title": "Remove Images Without Alt Tags", file: "js/web/accessibility/removeImagesWithoutAltTags.js", instant: false }, 9 | { menu: "W>A", "title": "Show Images Without Alt Tags", file: "js/web/accessibility/highlightImagesWithoutAltTags.js", instant: false }, 10 | { menu: "W>A", "title": "Remove Inputs Without Labels", file: "js/web/accessibility/removeInputsWithoutLabel.js", instant: false }, 11 | { menu: "W>A", "title": "Show Inputs Without Labels", file: "js/web/accessibility/highlightInputsWithoutLabel.js", instant: false }, 12 | { menu: "W>A", "title": "Remove Elements With Duplicate Ids", file: "js/web/accessibility/removeElementsWithTheSameId.js", instant: false }, 13 | { menu: "W>A", "title": "Show Elements With Duplicate Ids", file: "js/web/accessibility/highlightElementsWithTheSameId.js", instant: false }, 14 | { menu: "W>A", "title": "Remove Style Sheets", file: "js/web/accessibility/removeStyleSheets.js", instant: false }, 15 | { menu: "W>A", "title": "Visualise Tab Flow", file: "js/web/accessibility/visualiseTabFlow.js", instant: false }, 16 | { menu: "W", "title": "Exploits", menuref: "W>E", file: ""}, 17 | { menu: "W>E", "title": "Insert JS Script Injection in all inputs", file: "js/web/exploits/xss.js", instant: false }, 18 | { menu: "W>E", "title": "Insert SQL Injection in all inputs", file: "js/web/exploits/sql.js", instant: false }, 19 | { menu: "W", "title": "Styling", menuref: "W>S", file: ""}, 20 | { menu: "W>S", "title": "Check label text overflow", file: "js/web/styling/increaseLabelsText.js", instant: false }, 21 | { menu: "W>S", "title": "Check button text overflow", file: "js/web/styling/increaseButtonsText.js", instant: false }, 22 | { menu: "W>S", "title": "Check link text overflow", file: "js/web/styling/increaseLinksText.js", instant: false }, 23 | { menu: "W>S", "title": "Check page text overflow", file: "js/web/styling/increasePagesText.js", instant: false }, 24 | { menu: "W>S", "title": "Check all elements text overflow", file: "js/web/styling/increaseAllElementsText.js", instant: false }, 25 | { menu: "W", "title": "Utilities", menuref: "W>U", file: ""}, 26 | { menu: "W>U", "title": "Document Edit Mode On", file: "js/web/utilities/documentDesignModeOn.js", instant: false }, 27 | { menu: "W>U", "title": "Document Edit Mode Off", file: "js/web/utilities/documentDesignModeOff.js", instant: false }, 28 | { menu: "W>U", "title": "Pretty Print JSON String", file: "js/web/utilities/prettyPrintJsonToConsole.js", instant: false }, 29 | { menu: "W>U", "title": "Decode Base64 to console", file: "js/web/utilities/decodeBase64ToConsole.js", instant: false }, 30 | { menu: "W>U", "title": "Encode String as Base64 to console", file: "js/web/utilities/encodeBase64ToConsole.js", instant: false }, 31 | { menu: "W>U", "title": "For Every _element_ Do _this_", file: "js/web/utilities/forEveryDoThis.js", instant: false }, 32 | { menu: "W>U", "title": "Link Checker", file: "js/web/utilities/linkchecker.js", instant: false }, 33 | { menu: "W", "title": "Validation", menuref: "W>V", file: ""}, 34 | { menu: "W>V", "title": "Remove Max Length Attributes", file: "js/web/validation/removeMaxLength.js", instant: false }, 35 | { menu: "W>V", "title": "Remove Required Field Attributes", file: "js/web/validation/removeRequired.js", instant: false }, 36 | { menu: "W>V", "title": "Remove Paste Restrictions", file: "js/web/validation/removePasteRestrictions.js", instant: false }, 37 | { menu: "W>V", "title": "Change all input types to text", file: "js/web/validation/changeInputTypesToText.js", instant: false }, 38 | ]; -------------------------------------------------------------------------------- /extension/js/web/accessibility/highlightElementsWithTheSameId.js: -------------------------------------------------------------------------------- 1 | 2 | var ids = {}; 3 | var all = document.all || document.getElementsByTagName("*"); 4 | for (var i = 0, l = all.length; i < l; i++) { 5 | var id = all[i].id; 6 | if (id) { 7 | if (ids[id]) { 8 | all[i].style = all[i].style+"; border:10px dashed red;" 9 | document.getElementById(id).style = document.getElementById(id).style+"; border:10px dashed red;" 10 | } else { 11 | ids[id] = 1; 12 | } 13 | } 14 | }; -------------------------------------------------------------------------------- /extension/js/web/accessibility/highlightImagesWithoutAltTags.js: -------------------------------------------------------------------------------- 1 | Array.prototype.slice.call (document.querySelectorAll('img')).map(function(el){if(!el.alt){el.style = el.style+"; border:10px dashed red;"}}); -------------------------------------------------------------------------------- /extension/js/web/accessibility/highlightInputsWithoutLabel.js: -------------------------------------------------------------------------------- 1 | var inputs = document.querySelectorAll('input'); 2 | var labels = document.querySelectorAll('label'); 3 | 4 | for (var currentInput = 0; currentInput < inputs.length; currentInput++) { 5 | var inputHasLabel = false; 6 | for (var currentLabel = 0; currentLabel < labels.length; currentLabel++) { 7 | if (labels[currentLabel].htmlFor == inputs[currentInput].id) { 8 | inputHasLabel = true; 9 | } 10 | } 11 | if (inputHasLabel == false) { 12 | inputs[currentInput].style = inputs[currentInput].style+"; border:10px dashed red;"; 13 | } 14 | } -------------------------------------------------------------------------------- /extension/js/web/accessibility/removeElementsWithTheSameId.js: -------------------------------------------------------------------------------- 1 | 2 | var ids = {}; 3 | var all = document.all || document.getElementsByTagName("*"); 4 | for (var i = 0, l = all.length; i < l; i++) { 5 | var id = all[i].id; 6 | if (id) { 7 | if (ids[id]) { 8 | all[i].style.display = 'none'; 9 | document.getElementById(id).style.display = 'none'; 10 | } else { 11 | ids[id] = 1; 12 | } 13 | } 14 | }; -------------------------------------------------------------------------------- /extension/js/web/accessibility/removeImagesWithoutAltTags.js: -------------------------------------------------------------------------------- 1 | Array.prototype.slice.call (document.querySelectorAll('img')).map(function(el){if(!el.alt){el.src="Removed"}}); -------------------------------------------------------------------------------- /extension/js/web/accessibility/removeInputsWithoutLabel.js: -------------------------------------------------------------------------------- 1 | var inputs = document.querySelectorAll('input'); 2 | var labels = document.querySelectorAll('label'); 3 | 4 | for (var currentInput = 0; currentInput < inputs.length; currentInput++) { 5 | var inputHasLabel = false; 6 | for (var currentLabel = 0; currentLabel < labels.length; currentLabel++) { 7 | if (labels[currentLabel].htmlFor == inputs[currentInput].id) { 8 | inputHasLabel = true; 9 | } 10 | } 11 | if (inputHasLabel == false) { 12 | inputs[currentInput].style.display = 'none'; 13 | } 14 | } -------------------------------------------------------------------------------- /extension/js/web/accessibility/removeStyleSheets.js: -------------------------------------------------------------------------------- 1 | Array.prototype.slice.call (document.styleSheets).map(function(el){el.disabled = true;}); 2 | -------------------------------------------------------------------------------- /extension/js/web/accessibility/visualiseTabFlow.js: -------------------------------------------------------------------------------- 1 | // Get all inputs, buttons, links 2 | var inputs = Array.apply(null, document.querySelectorAll("input, select, button, a")).filter(elem => elem.getBoundingClientRect().left != 0) 3 | 4 | // Go through each input and get top/left 5 | for (I = 0; I < inputs.length; I++) { 6 | var currentInput = inputs[I].getBoundingClientRect(); 7 | var nextInput = null; 8 | 9 | if (I < inputs.length - 1) { 10 | nextInput = inputs[I + 1].getBoundingClientRect(); 11 | } else { 12 | lastInput = inputs[I]; 13 | nextInput = currentInput 14 | } 15 | 16 | // make line draw from middle of control instead of left handside of it 17 | var currentInputLeft = currentInput.left + (currentInput.width / 2) ; 18 | var currentInputTop = currentInput.top + (currentInput.height / 2); 19 | var nextInputLeft = nextInput.left + (nextInput.width / 2) ; 20 | var nextInputTop = nextInput.top + (nextInput.height / 2); 21 | 22 | // Draw a path from current input to next one using top/eft for elements 23 | // for first element we draw from top left to element 24 | if(I == 0) { 25 | document.body.insertAdjacentHTML('afterbegin', ''); 26 | } 27 | document.body.insertAdjacentHTML('afterbegin', ''); 28 | 29 | } -------------------------------------------------------------------------------- /extension/js/web/exploits/sql.js: -------------------------------------------------------------------------------- 1 | var textInputs = document.querySelectorAll('input[type=text]'); 2 | for(let i=0; i"; 5 | } -------------------------------------------------------------------------------- /extension/js/web/styling/increaseAllElementsText.js: -------------------------------------------------------------------------------- 1 | let elements = document.querySelectorAll('label, button, a, h1, h2, h3, h4, h5, p, input, select'); 2 | for(let i=0; i=linkReport.length){console.table(linkReport);clearInterval(finishReport)}}, 3000); 23 | -------------------------------------------------------------------------------- /extension/js/web/utilities/prettyPrintJsonToConsole.js: -------------------------------------------------------------------------------- 1 | console.log(JSON.stringify(JSON.parse(prompt("Enter JSON TO Pretty Print To Console","")), null, 2)); -------------------------------------------------------------------------------- /extension/js/web/validation/changeInputTypesToText.js: -------------------------------------------------------------------------------- 1 | var inputs = document.querySelectorAll('input'); 2 | for(let i=0; i