├── .DS_Store ├── .babelrc ├── .gitignore ├── LICENSE ├── Logo.svg ├── README.md ├── assets └── icons │ ├── mac │ └── icon.icns │ ├── png │ └── icon.png │ └── win │ └── icon.ico ├── main.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── assets │ └── css │ │ ├── App.css │ │ └── _example │ │ └── example.css ├── components │ ├── App.js │ ├── material │ │ ├── Accordion.js │ │ ├── AccordionMult.js │ │ ├── Card.js │ │ ├── Dialog.js │ │ ├── List.js │ │ ├── OpenSelect.js │ │ ├── SideNav.js │ │ └── spinner │ │ │ ├── Spinner.js │ │ │ ├── index.js │ │ │ └── spinner.css │ └── pages │ │ ├── About.js │ │ ├── History.js │ │ ├── Home.js │ │ └── Settings.js └── index.js ├── webpack.build.config.js ├── webpack.dev.config.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Breach/9392c2d9e2328a622fca4d176bb85f0c490e97a6/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", ["@babel/preset-react"]] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Breach 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 | -------------------------------------------------------------------------------- /Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 13 | 23 | 29 | 30 | 36 | 44 | 45 | 46 | 47 | 50 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 165 | 166 | 168 | 170 | 172 | 173 | 174 | 175 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![license](https://img.shields.io/github/license/oslabs-beta/Breach?color=blue) ![version](https://img.shields.io/badge/version-1.0.0-forestgreen) ![lastcommit](https://img.shields.io/github/last-commit/oslabs-beta/Breach?color=red) ![gitcontribute](https://img.shields.io/github/contributors/oslabs-beta/Breach) ![gitstars​](https://img.shields.io/github/stars/oslabs-beta/Breach?style=social) ![gitforks](https://img.shields.io/github/forks/oslabs-beta/Breach?style=social) 4 | 5 | Visit the Breach website [here](https://breachapplication.netlify.app/) 6 | 7 | ## Table of Contents 8 | - About Breach 9 | - Getting Started 10 | - Demo 11 | - Scan URL 12 | - Results 13 | - History 14 | - Settings 15 | - Looking Ahead 16 | - Contributors 17 | - License 18 | 19 | ## About 20 | 21 | Breach is a Cybersecurity desktop application intended to keep front-ends safe without doing damage. 22 | 23 | • One click for URL security test. 24 | 25 | • Saves history of previous tests 26 | 27 | • Customizable settings on color and font sizes for better user experience. 28 | 29 | ## Getting Started 30 | 31 | ### Download [Breach](https://breachapplication.netlify.app/) and [BreachServer](https://github.com/tommyedmunds/breachServer) 32 | 33 | The application can be downloaded for [windows](https://drive.google.com/file/d/145yAP5TmEQ5Ti9ohv3nl3cAHoqawgV2Q/view?usp=sharing) or [mac](https://drive.google.com/file/d/1kRA23lBGhJ-vXaD8gVmvyWeAOaZuZmFR/view?usp=sharing) (Linux option for packaging from codebase is not confirmed.) 34 | 35 | - For Mac OSX, right click on the downloaded file and click open. 36 | - For Windows users, simply open the .exe file to begin. 37 | 38 | The server can be forked and cloned to your local machine. Once it has been cloned, navigate to the correct folder in your terminal and 39 | 40 | - Type the following: 41 | 42 | ``` 43 | 44 | npm install 45 | 46 | npm start 47 | 48 | ``` 49 | Once you have these two items up and running you can get started or use our demo to learn more. We suggest changing the color of the application to your preference first. 50 | 51 | ## Demo 52 | 53 | Once you have opened [Breach](https://breachapplication.netlify.app/) and [BreachServer](https://github.com/oslabs-beta/BreachServer)... 54 | 55 | ### Scan URL 56 | Upon entering a URL the app sends out a request to the server, running tests for instances of innerHTML in the code, cookies and some XSS tests. 57 | #### Writing the URL for testing XSS 58 | - The URL must be formatted for a search query (have "q=") in order for the XSS tests to be run. 59 | 60 | ![image](https://miro.medium.com/max/480/0*I_4NbNZs3mlLrnEB) 61 | 62 | # 63 | ### Results 64 | When the app is finished loading, it will print your results. 65 | - Click on the defend logo to learn more about how to defend your app. 66 | 67 | ![image](https://miro.medium.com/max/480/0*mK9YJOS5qRCbsmj5) 68 | # 69 | ### History 70 | - You may view the history of results, change how many are shown, delete history items, or check out how to defend from attacks in the History tab of the application. 71 | 72 | ![image](https://miro.medium.com/max/480/0*PU2oFHccXRkpfyYW) 73 | # 74 | ### Settings 75 | - From the settings page you can change the color of the application to one of five different settings, as well as change the font size on the pages. 76 | 77 | ![image](https://miro.medium.com/max/480/0*ME6_mHgysoCqLbeh) 78 | 79 | ## Looking Ahead 80 | 81 | Breach is currently in its first release. The features we would like to implement in the future are: 82 | - Testing for SQL injection and DOS susceptibility. 83 | - Ability to export data for future use. 84 | - Display of active ports running on the server. 85 | - Integrate server into app. 86 | - Linux installer. 87 | 88 | ## Contributors 89 | 90 | [Jason Yoon](https://www.linkedin.com/in/jason-t-yoon/) [@Jason Yoon](https://github.com/jasony779) 91 | 92 | [Tommy Edmunds](https://www.linkedin.com/in/tommy-edmunds-a91aa41a9/) [@Tommy Edmunds](https://github.com/tommyedmunds) 93 | 94 | [Michael Geismar](https://www.linkedin.com/in/michael-geismar) [@michaelgeismar](https://github.com/MichaelGeismar) 95 | 96 | ## License 97 | 98 | MIT -- see [LICENSE.md](https://github.com/oslabs-beta/Breach/blob/main/LICENSE) file for more details. 99 | 100 | # 101 | This product is accelerated by [OS Labs](https://opensourcelabs.io/). 102 | -------------------------------------------------------------------------------- /assets/icons/mac/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Breach/9392c2d9e2328a622fca4d176bb85f0c490e97a6/assets/icons/mac/icon.icns -------------------------------------------------------------------------------- /assets/icons/png/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Breach/9392c2d9e2328a622fca4d176bb85f0c490e97a6/assets/icons/png/icon.png -------------------------------------------------------------------------------- /assets/icons/win/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Breach/9392c2d9e2328a622fca4d176bb85f0c490e97a6/assets/icons/win/icon.ico -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { app, BrowserWindow } = require('electron'); 3 | const path = require('path'); 4 | const url = require('url'); 5 | const { ipcMain } = require('electron'); 6 | const Store = require('electron-store'); 7 | const store = new Store(); 8 | 9 | //local storage working 10 | 11 | // Keep a global reference of the window object, if you don't, the window will 12 | // be closed automatically when the JavaScript object is garbage collected. 13 | 14 | let mainWindow; 15 | 16 | // Keep a reference for dev mode 17 | let dev = false; 18 | 19 | if (process.env.NODE_ENV !== undefined && process.env.NODE_ENV === 'development') { 20 | dev = true; 21 | } 22 | 23 | // Temporary fix broken high-dpi scale factor on Windows (125% scaling) 24 | if (process.platform === 'win32') { 25 | app.commandLine.appendSwitch('high-dpi-support', 'true'); 26 | app.commandLine.appendSwitch('force-device-scale-factor', '1'); 27 | } 28 | 29 | function createWindow() { 30 | // Create the browser window. 31 | mainWindow = new BrowserWindow({ 32 | width: 1200, 33 | height: 1000, 34 | show: false, 35 | webPreferences: { 36 | nodeIntegration: true, 37 | contextIsolation: false, 38 | }, 39 | }); 40 | 41 | // and load the index.html of the app. 42 | let indexPath; 43 | 44 | if (dev && process.argv.indexOf('--noDevServer') === -1) { 45 | indexPath = url.format({ 46 | protocol: 'http:', 47 | host: 'localhost:8080', 48 | pathname: 'index.html', 49 | slashes: true, 50 | }); 51 | } else { 52 | indexPath = url.format({ 53 | protocol: 'file:', 54 | //change back to "dist" and "index.html" after webpack rebuild or for production build 55 | pathname: path.join(__dirname, 'dist', 'index.html'), 56 | slashes: true, 57 | }); 58 | } 59 | 60 | mainWindow.loadURL(indexPath); 61 | 62 | ipcMain.on('asynchronous-message', (event, arg) => { 63 | if (typeof arg.value === 'number' && arg.value.toString().length === 1) { 64 | arg.name = 'historyLength'; 65 | } else { 66 | const regex = /\d/g; 67 | 68 | if (regex.test(arg.value)) { 69 | arg.name = 'fontSize'; 70 | } else { 71 | arg.name = 'theme'; 72 | } 73 | } 74 | store.set(arg.name, arg.value); 75 | event.reply('asynchronous-reply', 'pong'); 76 | }); 77 | 78 | if (!store.get('fontSize') || typeof store.get('fontSize') !== 'string') 79 | store.set('fontSize', '16px'); 80 | if (!store.get('history')) store.set('history', []); 81 | if (!store.get('historyLength')) store.set('historyLength', 3); 82 | const dark = { 83 | overrides: { 84 | MuiCssBaseline: { 85 | '@global': { 86 | // MUI typography elements use REMs, so you can scale the global 87 | // font size by setting the font-size on the element. 88 | html: { 89 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 90 | }, 91 | }, 92 | }, 93 | }, 94 | palette: { 95 | type: 'dark', 96 | }, 97 | }; 98 | const light = { 99 | overrides: { 100 | MuiCssBaseline: { 101 | '@global': { 102 | html: { 103 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 104 | }, 105 | }, 106 | }, 107 | text: { 108 | primary: 'rgba(0, 0, 0, 1)', 109 | secondary: 'rgba(255, 255, 255, 1)', 110 | disabled: 'rgba(255, 255, 255, 1)', 111 | hint: 'rgba(0, 0, 0, 0.38)', 112 | }, 113 | }, 114 | palette: { 115 | type: 'light', 116 | }, 117 | }; 118 | const blue = { 119 | overrides: { 120 | MuiCssBaseline: { 121 | '@global': { 122 | html: { 123 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 124 | }, 125 | }, 126 | }, 127 | }, 128 | palette: { 129 | common: { black: 'rgba(0, 0, 0, 1)', white: 'rgba(255, 255, 255, 1)' }, 130 | background: { 131 | paper: 'rgba(25, 161, 200, 1)', 132 | default: 'rgba(42, 132, 157, 1)', 133 | }, 134 | primary: { 135 | light: 'rgba(147, 159, 255, 1)', 136 | main: 'rgba(52, 72, 205, 1)', 137 | dark: 'rgba(1, 22, 155, 1)', 138 | contrastText: 'rgba(255, 255, 255, 1)', 139 | }, 140 | secondary: { 141 | light: 'rgba(129, 182, 244, 1)', 142 | main: 'rgba(122, 178, 242, 1)', 143 | dark: 'rgba(0, 50, 110, 1)', 144 | contrastText: 'rgba(255, 255, 255, 1)', 145 | }, 146 | error: { 147 | light: 'rgba(135, 188, 251, 1)', 148 | main: 'rgba(16, 64, 120, 1)', 149 | dark: 'rgba(22, 45, 73, 1)', 150 | contrastText: '#fff', 151 | }, 152 | text: { 153 | primary: 'rgb(255, 255, 255, .9)', 154 | secondary: 'rgb(255, 255, 255, .7)', 155 | disabled: 'rgba(255, 255, 255, 1)', 156 | hint: 'rgba(0, 0, 0, 0.38)', 157 | }, 158 | }, 159 | }; 160 | const purple = { 161 | overrides: { 162 | MuiCssBaseline: { 163 | '@global': { 164 | html: { 165 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 166 | }, 167 | }, 168 | }, 169 | }, 170 | palette: { 171 | common: { black: 'rgba(0, 0, 0, 1)', white: 'rgba(255, 255, 255, 1)' }, 172 | background: { 173 | paper: 'rgb(172, 130, 234, 1)', 174 | default: 'rgba(149, 115, 215, 1)', 175 | }, 176 | primary: { 177 | light: 'rgba(156, 0, 220, 1)', 178 | main: 'rgba(112, 0, 193, 1)', 179 | dark: 'rgba(76, 1, 125, 1)', 180 | contrastText: 'rgba(255, 255, 255, 1)', 181 | }, 182 | secondary: { 183 | light: 'rgba(181, 94, 222, 1)', 184 | main: 'rgba(72, 0, 150, 1)', 185 | dark: 'rgba(78, 0, 110, 1)', 186 | contrastText: 'rgba(255, 255, 255, 1)', 187 | }, 188 | error: { 189 | light: 'rgba(135, 188, 251, 1)', 190 | main: 'rgba(174, 98, 244, 1)', 191 | dark: 'rgba(22, 45, 73, 1)', 192 | contrastText: '#fff', 193 | }, 194 | text: { 195 | primary: 'rgb(255, 255, 255, .9)', 196 | secondary: 'rgb(255, 255, 255, .7)', 197 | disabled: 'rgba(255, 255, 255, 0.38)', 198 | hint: 'rgba(255, 255, 255, 0.38)', 199 | }, 200 | }, 201 | }; 202 | const green = { 203 | overrides: { 204 | MuiCssBaseline: { 205 | '@global': { 206 | html: { 207 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 208 | }, 209 | }, 210 | }, 211 | }, 212 | palette: { 213 | common: { black: 'rgba(0, 0, 0, 1)', white: 'rgba(255, 255, 255, 1)' }, 214 | background: { 215 | paper: 'rgba(55, 158, 132, 1)', 216 | default: 'rgba(35, 138, 112, 1)', 217 | }, 218 | primary: { 219 | light: 'rgba(151, 254, 32, 1)', 220 | main: 'rgba(21, 87, 63, 1)', 221 | dark: 'rgba(44, 81, 4, 1)', 222 | contrastText: 'rgba(255, 255, 255, 1)', 223 | }, 224 | secondary: { 225 | light: 'rgba(85, 255, 196, 1)', 226 | main: 'rgba(0, 97, 63, 1)', 227 | dark: 'rgba(0, 95, 63, 1)', 228 | contrastText: 'rgba(255, 255, 255, 1)', 229 | }, 230 | error: { 231 | light: 'rgba(0, 255, 167, 1)', 232 | main: 'rgba(0, 203, 133, 1)', 233 | dark: 'rgba(0, 112, 73, 1)', 234 | contrastText: '#fff', 235 | }, 236 | text: { 237 | primary: 'rgb(255, 255, 255, .9)', 238 | secondary: 'rgb(255, 255, 255, .7)', 239 | disabled: 'rgba(255, 255, 255, 0.38)', 240 | hint: 'rgba(255, 255, 255, 0.38)', 241 | }, 242 | }, 243 | }; 244 | 245 | if (!store.get('fontSize')) store.set('fontSize', '16px'); 246 | 247 | ipcMain.on('load-data', function (event, arg) { 248 | 249 | if (arg && typeof arg.options[0] === 'number') { 250 | mainWindow.webContents.send('data-reply', store.store); 251 | } else { 252 | if (store.get('fontSize') === null || store.get('fontSize') === undefined) { 253 | dark.overrides.MuiCssBaseline['@global'].html.fontSize = 16; 254 | light.overrides.MuiCssBaseline['@global'].html.fontSize = 16; 255 | } 256 | const dark = { 257 | overrides: { 258 | MuiCssBaseline: { 259 | '@global': { 260 | html: { 261 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 262 | }, 263 | }, 264 | }, 265 | }, 266 | palette: { 267 | type: 'dark', 268 | }, 269 | }; 270 | const light = { 271 | overrides: { 272 | MuiCssBaseline: { 273 | '@global': { 274 | html: { 275 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 276 | }, 277 | }, 278 | }, 279 | }, 280 | palette: { 281 | type: 'light', 282 | }, 283 | }; 284 | const blue = { 285 | overrides: { 286 | MuiCssBaseline: { 287 | '@global': { 288 | html: { 289 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 290 | }, 291 | }, 292 | }, 293 | }, 294 | palette: { 295 | common: { black: 'rgba(0, 0, 0, 1)', white: 'rgba(255, 255, 255, 1)' }, 296 | background: { 297 | paper: 'rgba(25, 161, 200, 1)', 298 | default: 'rgba(42, 132, 157, 1)', 299 | }, 300 | primary: { 301 | light: 'rgba(147, 159, 255, 1)', 302 | main: 'rgba(52, 72, 205, 1)', 303 | dark: 'rgba(1, 22, 155, 1)', 304 | contrastText: 'rgba(255, 255, 255, 1)', 305 | }, 306 | secondary: { 307 | light: 'rgba(129, 182, 244, 1)', 308 | main: 'rgba(122, 178, 242, 1)', 309 | dark: 'rgba(0, 50, 110, 1)', 310 | contrastText: 'rgba(255, 255, 255, 1)', 311 | }, 312 | error: { 313 | light: 'rgba(135, 188, 251, 1)', 314 | main: 'rgba(16, 64, 120, 1)', 315 | dark: 'rgba(22, 45, 73, 1)', 316 | contrastText: '#fff', 317 | }, 318 | text: { 319 | primary: 'rgb(255, 255, 255, .9)', 320 | secondary: 'rgb(255, 255, 255, .7)', 321 | disabled: 'rgba(255, 255, 255, 1)', 322 | hint: 'rgba(0, 0, 0, 0.38)', 323 | }, 324 | }, 325 | }; 326 | const purple = { 327 | overrides: { 328 | MuiCssBaseline: { 329 | '@global': { 330 | html: { 331 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 332 | }, 333 | }, 334 | }, 335 | }, 336 | palette: { 337 | common: { black: 'rgba(0, 0, 0, 1)', white: 'rgba(255, 255, 255, 1)' }, 338 | background: { 339 | paper: 'rgb(172, 130, 234, 1)', 340 | default: 'rgba(149, 115, 215, 1)', 341 | }, 342 | primary: { 343 | light: 'rgba(156, 0, 220, 1)', 344 | main: 'rgba(112, 0, 193, 1)', 345 | dark: 'rgba(76, 1, 125, 1)', 346 | contrastText: 'rgba(255, 255, 255, 1)', 347 | }, 348 | secondary: { 349 | light: 'rgba(181, 94, 222, 1)', 350 | main: 'rgba(72, 0, 150, 1)', 351 | dark: 'rgba(78, 0, 110, 1)', 352 | contrastText: 'rgba(255, 255, 255, 1)', 353 | }, 354 | error: { 355 | light: 'rgba(135, 188, 251, 1)', 356 | main: 'rgba(174, 98, 244, 1)', 357 | dark: 'rgba(22, 45, 73, 1)', 358 | contrastText: '#fff', 359 | }, 360 | text: { 361 | primary: 'rgb(255, 255, 255, .9)', 362 | secondary: 'rgb(255, 255, 255, .7)', 363 | disabled: 'rgba(255, 255, 255, 0.38)', 364 | hint: 'rgba(255, 255, 255, 0.38)', 365 | }, 366 | }, 367 | }; 368 | const green = { 369 | overrides: { 370 | MuiCssBaseline: { 371 | '@global': { 372 | html: { 373 | fontSize: parseInt(store.get('fontSize').slice(0, 2)), 374 | }, 375 | }, 376 | }, 377 | }, 378 | palette: { 379 | common: { black: 'rgba(0, 0, 0, 1)', white: 'rgba(255, 255, 255, 1)' }, 380 | background: { 381 | paper: 'rgba(55, 158, 132, 1)', 382 | default: 'rgba(35, 138, 112, 1)', 383 | }, 384 | primary: { 385 | light: 'rgba(151, 254, 32, 1)', 386 | main: 'rgba(21, 87, 63, 1)', 387 | dark: 'rgba(44, 81, 4, 1)', 388 | contrastText: 'rgba(255, 255, 255, 1)', 389 | }, 390 | secondary: { 391 | light: 'rgba(85, 255, 196, 1)', 392 | main: 'rgba(0, 97, 63, 1)', 393 | dark: 'rgba(0, 95, 63, 1)', 394 | contrastText: 'rgba(255, 255, 255, 1)', 395 | }, 396 | error: { 397 | light: 'rgba(0, 255, 167, 1)', 398 | main: 'rgba(0, 203, 133, 1)', 399 | dark: 'rgba(0, 112, 73, 1)', 400 | contrastText: '#fff', 401 | }, 402 | text: { 403 | primary: 'rgb(255, 255, 255, .9)', 404 | secondary: 'rgb(255, 255, 255, .7)', 405 | disabled: 'rgba(255, 255, 255, 0.38)', 406 | hint: 'rgba(255, 255, 255, 0.38)', 407 | }, 408 | }, 409 | }; 410 | 411 | store.set('purple', purple); 412 | 413 | store.set('green', green); 414 | 415 | store.set('dark', dark); 416 | 417 | store.set('light', light); 418 | 419 | store.set('blue', blue); 420 | 421 | mainWindow.webContents.send('data-reply', store.store); 422 | } 423 | }); 424 | 425 | // Don't show until we are ready and loaded 426 | mainWindow.once('ready-to-show', () => { 427 | mainWindow.show(); 428 | 429 | // Open the DevTools automatically if developing 430 | if (dev) { 431 | const { 432 | default: installExtension, 433 | REACT_DEVELOPER_TOOLS, 434 | } = require('electron-devtools-installer'); 435 | 436 | installExtension(REACT_DEVELOPER_TOOLS).catch((err) => 437 | console.error('Error loading React DevTools: ', err) 438 | ); 439 | mainWindow.webContents.openDevTools(); 440 | } 441 | }); 442 | 443 | // Emitted when the window is closed. 444 | mainWindow.on('closed', function () { 445 | // Dereference the window object, usually you would store windows 446 | // in an array if your app supports multi windows, this is the time 447 | // when you should delete the corresponding element. 448 | mainWindow = null; 449 | }); 450 | } 451 | 452 | // This method will be called when Electron has finished 453 | // initialization and is ready to create browser windows. 454 | // Some APIs can only be used after this event occurs. 455 | app.on('ready', createWindow); 456 | 457 | // Quit when all windows are closed. 458 | app.on('window-all-closed', () => { 459 | // On macOS it is common for applications and their menu bar 460 | // to stay active until the user quits explicitly with Cmd + Q 461 | if (process.platform !== 'darwin') { 462 | app.quit(); 463 | } 464 | }); 465 | 466 | app.on('activate', () => { 467 | // On macOS it's common to re-create a window in the app when the 468 | // dock icon is clicked and there are no other windows open. 469 | if (mainWindow === null) { 470 | createWindow(); 471 | } 472 | }); 473 | 474 | ipcMain.on('url', function (event, arg) { 475 | // let test = { 476 | // url: arg, 477 | // cookieTest: '', 478 | // jqueryTest: false, 479 | // jsXSS: false, 480 | // }; 481 | 482 | let history; 483 | store.get('history') ? (history = store.get('history')) : (history = []); 484 | history.unshift(arg); 485 | history.length >= 25 ? history.pop() : history; 486 | store.set('history', history); 487 | mainWindow.webContents.send('testOutput', arg); 488 | }); 489 | 490 | ipcMain.on('clearHistory', function (event, arg) { 491 | store.set('history', []); 492 | mainWindow.webContents.send('historyCleared', store.get('history')); 493 | }); 494 | 495 | ipcMain.on('clearItem', function (event, arg) { 496 | let newHistory = store.get('history'); 497 | newHistory.splice(arg, 1); 498 | store.set('history', newHistory); 499 | mainWindow.webContents.send('itemCleared', store.get('history')); 500 | }); 501 | 502 | ipcMain.on('getHistoryLength', function (event, arg) { 503 | if (!store.get('historyLength')) store.set('historyLength', 3); 504 | mainWindow.webContents.send('length', store.get('historyLength')); 505 | }); 506 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Breach", 3 | "productName": "Breach", 4 | "version": "1.0.0", 5 | "description": "A cyber-sec tool to be used responsibly in identifying XSS vulnerabilities", 6 | "license": "MIT", 7 | "private": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/oslabs-beta/Breach" 11 | }, 12 | "author": { 13 | "name": "Jason Yoon, Michael Geismar, Tommy Edmunds", 14 | "email": "jasony779@gmail.com", 15 | "url": "https://github.com/oslabs-beta/Breach" 16 | }, 17 | "keywords": [ 18 | "app", 19 | "boilerplate", 20 | "electron", 21 | "open", 22 | "open-source", 23 | "postcss", 24 | "react", 25 | "reactjs", 26 | "source", 27 | "webpack" 28 | ], 29 | "engines": { 30 | "node": ">=9.0.0", 31 | "npm": ">=5.0.0", 32 | "yarn": ">=1.0.0" 33 | }, 34 | "browserslist": [ 35 | "last 4 versions" 36 | ], 37 | "main": "main.js", 38 | "scripts": { 39 | "prod": "cross-env NODE_ENV=production webpack --mode production --config webpack.build.config.js && electron --noDevServer .", 40 | "start": "cross-env NODE_ENV=development webpack serve --hot --host 0.0.0.0 --config=./webpack.dev.config.js --mode development", 41 | "build": "cross-env NODE_ENV=production webpack --config webpack.build.config.js --mode production", 42 | "dev": "concurrently \"cross-env NODE_ENV=development --config=./webpack.dev.config.js\" \"NODE_ENV=development webpack serve --open\"", 43 | "package": "npm run build", 44 | "postpackage": "electron-packager ./ --out=./builds", 45 | "package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=assets/icons/mac/icon.icns --prune=true --out=release-builds", 46 | "package-win": "electron-packager . --overwrite --asar --platform=win32 --arch=ia32 --icon=assets/icons/win/icon --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Breach\"", 47 | "package-linux": "electron-packager . electron-tutorial-app --overwrite --asar=true --platform=linux --arch=x64 --icon=Icons/png/icon.png --prune=true --out=release-builds" 48 | }, 49 | "dependencies": { 50 | "@fortawesome/fontawesome-svg-core": "^1.2.35", 51 | "@fortawesome/free-brands-svg-icons": "^5.15.3", 52 | "@fortawesome/free-regular-svg-icons": "^5.15.3", 53 | "@fortawesome/free-solid-svg-icons": "^5.15.3", 54 | "@fortawesome/react-fontawesome": "^0.1.14", 55 | "@material-ui/core": "^4.12.1", 56 | "@material-ui/icons": "^4.11.2", 57 | "@types/react-transition-group": "^4.4.2", 58 | "axios": "^0.21.1", 59 | "concurrently": "^6.2.0", 60 | "electron-fetch": "^1.7.3", 61 | "electron-storage": "^1.0.7", 62 | "electron-store": "^8.0.0", 63 | "jsdom": "^16.6.0", 64 | "nodemon": "^2.0.12", 65 | "postcss": "^8.2.8", 66 | "puppeteer-core": "^10.1.0", 67 | "puppeteer-in-electron": "^3.0.5", 68 | "react": "^17.0.1", 69 | "react-axios": "^2.0.5", 70 | "react-dom": "^17.0.1", 71 | "react-loader-spinner": "^4.0.0", 72 | "react-promise-tracker": "^2.1.0", 73 | "react-router": "^5.2.0", 74 | "react-router-dom": "^5.2.0", 75 | "react-spinners": "^0.11.0" 76 | }, 77 | "devDependencies": { 78 | "@babel/core": "^7.13.10", 79 | "@babel/preset-env": "^7.13.10", 80 | "@babel/preset-react": "^7.12.13", 81 | "@svgr/webpack": "^5.5.0", 82 | "babel-loader": "^8.2.2", 83 | "cross-env": "^7.0.3", 84 | "css-loader": "^5.1.1", 85 | "electron": "^12.0.15", 86 | "electron-devtools-installer": "^3.1.1", 87 | "electron-packager": "^15.2.0", 88 | "electron-reloader": "^1.2.1", 89 | "file-loader": "^6.2.0", 90 | "html-webpack-plugin": "^5.3.1", 91 | "mini-css-extract-plugin": "^1.3.9", 92 | "postcss-import": "^14.0.0", 93 | "postcss-loader": "^5.1.0", 94 | "postcss-nested": "^5.0.5", 95 | "postcss-preset-env": "^6.7.0", 96 | "postcss-pxtorem": "^5.1.1", 97 | "style-loader": "^2.0.0", 98 | "webpack": "^5.24.4", 99 | "webpack-cli": "^4.5.0", 100 | "webpack-dev-server": "^3.11.2" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'postcss-nested': {}, 5 | 'postcss-preset-env': {}, 6 | 'postcss-pxtorem': { 7 | rootValue: 16, 8 | unitPrecision: 5, 9 | propList: ['*'], 10 | selectorBlackList: ['html', 'body'], 11 | replace: true, 12 | mediaQuery: false, 13 | minPixelValue: 0 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/assets/css/App.css: -------------------------------------------------------------------------------- 1 | /* Main CSS file */ 2 | 3 | @import '_example/example.css'; 4 | 5 | .settingsDiv { 6 | margin-left: 30px; 7 | } 8 | 9 | .settingsDiv > button { 10 | margin-left: 1%; 11 | } 12 | 13 | #historyDiv { 14 | /* margin-left: 30px; */ 15 | overflow: visible !important; 16 | } 17 | 18 | .fade-enter { 19 | opacity: 0; 20 | } 21 | 22 | .fade-enter.fade-enter-active { 23 | opacity: 1; 24 | transition: opacity 5ms ease-in; 25 | } 26 | 27 | body { 28 | margin: 2% 5% 0% 5% !important; 29 | } 30 | 31 | .home-button-margin { 32 | margin: 3%; 33 | } 34 | 35 | .results-grid-home { 36 | padding: 1%; 37 | margin: 1%; 38 | display: grid; 39 | grid-template-columns: 1fr 1fr; 40 | grid-template-rows: 1fr 1fr 1fr 1fr; 41 | gap: 10% 0%; 42 | justify-items: left; 43 | align-items: center; 44 | } 45 | 46 | /* if results in history dont fit well, make this a flex-box with set width and height and give it a flex wrap */ 47 | .results-grid-history { 48 | padding: 1%; 49 | margin: 1%; 50 | display: grid; 51 | grid-template-columns: 1fr 1fr; 52 | grid-template-rows: 1fr 1fr 1fr 1fr; 53 | gap: 0.5% 0%; 54 | justify-items: left; 55 | align-items: center; 56 | } 57 | 58 | .historyCard { 59 | height: 80%; 60 | } 61 | 62 | .history-grid { 63 | padding: 0% 0% 5% 0px; 64 | display: grid; 65 | grid-template-columns: 50% 50%; 66 | gap: 2% 2%; 67 | } 68 | 69 | .accordion-column { 70 | display: flex; 71 | flex-direction: column; 72 | } 73 | 74 | .history-flex { 75 | display: flex; 76 | margin: 15px 10px; 77 | /* height: 22%; */ 78 | flex-direction: row; 79 | align-items: center; 80 | align-content: center; 81 | justify-content: space-around; 82 | justify-items: baseline; 83 | } 84 | 85 | .history-button-margin { 86 | margin: 5px; 87 | } 88 | 89 | .history-bottom { 90 | width: fit-content; 91 | display: flex; 92 | justify-content: center; 93 | align-items: center; 94 | } 95 | 96 | #filled-basic { 97 | width: 50vw !important; 98 | } 99 | 100 | .settings-paper { 101 | display: flex; 102 | flex-direction: column; 103 | justify-content: center; 104 | align-items: center; 105 | padding: 3%; 106 | margin: 4% 15% !important; 107 | } 108 | 109 | .export-button { 110 | width: fit-content; 111 | margin: 0% 4.5%; 112 | } 113 | 114 | .about-paper { 115 | display: flex; 116 | flex-direction: column; 117 | margin: 5%; 118 | } 119 | 120 | .MuiPaper-elevation3 { 121 | /* margin: 2.5% 6%; */ 122 | padding: 2%; 123 | margin: 20px 0px; 124 | display: flex; 125 | /* flex-direction: column; 126 | justify-content: center; 127 | align-items: center; 128 | align-content: center; 129 | justify-items: center; */ 130 | } 131 | 132 | .lmargin-home { 133 | margin: 4% 6%; 134 | overflow: hidden; 135 | } 136 | 137 | .inside-paper { 138 | padding: 15px; 139 | width: 75%; 140 | /* height: 15vh; */ 141 | /* margin: 0% 1% 0% 5%; */ 142 | display: flex; 143 | align-items: center; 144 | align-content: center; 145 | justify-content: center; 146 | justify-items: center; 147 | /* padding: 1%; */ 148 | } 149 | 150 | .inside-paper-bottom { 151 | /* margin: 0% 0% 0.5% 5%; */ 152 | height: fit-content !important; 153 | display: flex; 154 | align-items: stretch; 155 | /* width: 70%; */ 156 | } 157 | 158 | .MuiPaper-root { 159 | } 160 | 161 | .top-home { 162 | width: 50vw; 163 | } 164 | 165 | .MuiInputBase-input { 166 | width: 55vw; 167 | } 168 | 169 | .mainContainer { 170 | height: 100%; 171 | width: 100%; 172 | } 173 | 174 | .whole-cards { 175 | height: fit-content; 176 | } 177 | 178 | .top-div { 179 | display: flex; 180 | flex-direction: column; 181 | justify-content: center; 182 | align-items: center; 183 | width: fit-content; 184 | } 185 | 186 | .spin { 187 | padding-top: 10%; 188 | } 189 | 190 | .logo-top { 191 | width: 1.3in; 192 | fill: white; 193 | position: absolute; 194 | left: 5.75%; 195 | top: -3.1%; 196 | z-index: 1120; 197 | } 198 | 199 | .logo-top-sideNav { 200 | width: 1.3in; 201 | fill: white; 202 | position: absolute; 203 | left: 7.75%; 204 | top: -45%; 205 | z-index: 1120; 206 | } 207 | 208 | .logo-history { 209 | width: 80px; 210 | fill: white; 211 | /* margin-left: 38.6vw; */ 212 | } 213 | 214 | .center-logo { 215 | display: flex; 216 | justify-content: center; 217 | align-items: center; 218 | } 219 | 220 | .logo-bottom { 221 | width: 13%; 222 | fill: white; 223 | /* position: absolute; 224 | left: 45.2%; 225 | top: 83vh; */ 226 | } 227 | 228 | .logo-bottom-regular { 229 | width: 15%; 230 | /* background-color: #3f51b5; */ 231 | fill: black; 232 | } 233 | 234 | /* @media screen and (max-width: 650px), (max-height: 820px) { 235 | body { 236 | overflow: auto !important; 237 | margin: 0% !important; 238 | } 239 | 240 | .MuiPaper-elevation3 { 241 | margin: 1% 0%; 242 | 243 | display: flex; 244 | } 245 | 246 | .lmargin-home { 247 | height: 100vh; 248 | width: 100vw; 249 | } 250 | 251 | .inside-paper { 252 | width: 95%; 253 | display: flex; 254 | align-items: center; 255 | align-content: center; 256 | justify-content: center; 257 | justify-items: center; 258 | 259 | } 260 | 261 | .inside-paper-bottom { 262 | 263 | height: 60vh !important; 264 | display: flex; 265 | align-items:stretch; 266 | 267 | } 268 | 269 | .results-grid { 270 | padding: 1%; 271 | margin: 1%; 272 | display: grid; 273 | grid-template-columns: 1fr 1fr; 274 | grid-template-rows: 1fr 1fr 1fr 1fr; 275 | gap: 10% 0%; 276 | justify-items: center; 277 | align-items: center; 278 | } 279 | 280 | } */ 281 | /* @media screen and (max-width: 300px) and (max-height: 740px) { 282 | body { 283 | overflow: auto; 284 | } 285 | .MuiPaper-elevation3 { 286 | margin: 5% 5%; 287 | padding: 3%; 288 | display: flex; 289 | height: 900px; 290 | width: auto; 291 | } 292 | 293 | .inside-paper { 294 | width: auto; 295 | display: flex; 296 | align-items: center; 297 | align-content: center; 298 | justify-content: center; 299 | justify-items: center; 300 | } 301 | 302 | .inside-paper-bottom { 303 | height: 500px !important; 304 | display: flex; 305 | align-items:stretch; 306 | } 307 | 308 | .results-grid { 309 | padding: 1%; 310 | margin: 1%; 311 | display: grid; 312 | grid-template-columns: 1fr 1fr; 313 | grid-template-rows: 1fr 1fr 1fr 1fr; 314 | gap: 10% 0%; 315 | justify-items: center; 316 | align-items: center; 317 | } 318 | } */ 319 | -------------------------------------------------------------------------------- /src/assets/css/_example/example.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;1,100&display=swap'); 2 | 3 | @media screen and (prefers-color-scheme: light), screen and (prefers-color-scheme: no-preference) { 4 | Light theme 5 | body{ 6 | color: #000; 7 | background-color: #fff; 8 | overflow: hidden; 9 | } 10 | } 11 | 12 | @media screen and (prefers-color-scheme: dark) { 13 | Dark theme 14 | body { 15 | color: #fff; 16 | background-color: #000; 17 | } 18 | } 19 | 20 | * { 21 | font-family: 'Roboto', sans-serif;; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Switch, Route, Redirect, useLocation } from 'react-router-dom'; 3 | 4 | import '../assets/css/App.css'; 5 | import PermanentDrawerLeft from './material/SideNav'; 6 | import About from './pages/About'; 7 | import History from './pages/History'; 8 | import Home from './pages/Home'; 9 | import Settings from './pages/Settings'; 10 | import { TransitionGroup, CSSTransition } from 'react-transition-group'; 11 | 12 | import { library } from '@fortawesome/fontawesome-svg-core'; 13 | import { fab } from '@fortawesome/free-brands-svg-icons'; 14 | import { faCheckSquare, faCoffee } from '@fortawesome/free-solid-svg-icons'; 15 | import { fas } from '@fortawesome/free-solid-svg-icons'; 16 | 17 | library.add(fab,fas,faCheckSquare, faCoffee) 18 | 19 | function App() { 20 | let location = useLocation(); 21 | 22 | const RedirectToHome = () => { 23 | return ; 24 | }; 25 | RedirectToHome() 26 | 27 | return ( 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | { 36 | return () 37 | }}/> 38 | 39 |
40 | ); 41 | } 42 | 43 | export default App; 44 | -------------------------------------------------------------------------------- /src/components/material/Accordion.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import Accordion from '@material-ui/core/Accordion'; 4 | import AccordionSummary from '@material-ui/core/AccordionSummary'; 5 | import AccordionDetails from '@material-ui/core/AccordionDetails'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; 8 | import NestedList from './List'; 9 | import { props } from 'bluebird'; 10 | import AccordionMult from './AccordionMult' 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | root: { 14 | width: '100%', 15 | }, 16 | heading: { 17 | fontSize: theme.typography.pxToRem(15), 18 | fontWeight: theme.typography.fontWeightRegular, 19 | }, 20 | })); 21 | 22 | export default function SimpleAccordion(props) { 23 | const classes = useStyles(); 24 | return ( 25 |
26 | 27 | } 29 | aria-controls='panel1a-content' 30 | id='panel1a-header' 31 | > 32 | Open for Cookie Results 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |

41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/components/material/AccordionMult.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import Accordion from '@material-ui/core/Accordion'; 4 | import AccordionSummary from '@material-ui/core/AccordionSummary'; 5 | import AccordionDetails from '@material-ui/core/AccordionDetails'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; 8 | import NestedList from './List'; 9 | import { props } from 'bluebird'; 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | root: { 13 | width: '100%', 14 | }, 15 | heading: { 16 | fontSize: theme.typography.pxToRem(15), 17 | fontWeight: theme.typography.fontWeightRegular, 18 | }, 19 | })); 20 | 21 | export default function AccordionMult(props) { 22 | const classes = useStyles(); 23 | 24 | return props.cookiesArr.map((el, i) => { 25 | return ( 26 |
27 | 28 | } 30 | aria-controls='panel1a-content' 31 | id='panel1a-header' 32 | > 33 | Cookie Result {i} 34 | 35 | 36 | 37 | 38 | 39 | 40 |

41 |
42 | ); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /src/components/material/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import Card from '@material-ui/core/Card'; 4 | import CardActions from '@material-ui/core/CardActions'; 5 | import CardContent from '@material-ui/core/CardContent'; 6 | import Button from '@material-ui/core/Button'; 7 | import Typography from '@material-ui/core/Typography'; 8 | import NestedList from './List'; 9 | 10 | import SimpleAccordion from './Accordion'; 11 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 12 | import { ReactComponent as Logo } from '../../../Logo.svg' 13 | 14 | const useStyles = makeStyles({ 15 | root: { 16 | // backgroundImage: 'url(Logo.svg)', 17 | minWidth: 275, 18 | fontSize: 10, 19 | minHeight: 350, 20 | width: '100%', 21 | // height: 'fit-content', 22 | // width: '100%', 23 | // margin: '0% 0% 6% 0%' 24 | // overflow: 'hidden' 25 | }, 26 | bullet: { 27 | display: 'inline-block', 28 | margin: '0 2px', 29 | transform: 'scale(0.8)', 30 | }, 31 | title: { 32 | fontSize: '0.8rem', 33 | }, 34 | pos: { 35 | marginBottom: 12, 36 | }, 37 | }); 38 | 39 | export default function SimpleCard(props) { 40 | const classes = useStyles(); 41 | 42 | const disclaimer = () => { 43 | if (!props.url.includes('=')) { 44 | return ( 45 | 46 |

* XSS attack tests require a query ('=') in the url *

47 |

48 |
49 | ); 50 | } 51 | }; 52 | const domain = () => { 53 | let count = 0; 54 | let split = props.url; 55 | let output = ''; 56 | for (let i = 0; i < split.length; i++) { 57 | if (split[i] === '/') count += 1; 58 | if (count >= 3) { 59 | break; 60 | } 61 | output += split[i]; 62 | } 63 | return output; 64 | }; 65 | 66 | return ( 67 | 68 | 69 | 70 | Results for {domain()} 71 | 72 | 73 | 74 | : {props.currentTime} 75 | 76 | 77 | 78 | {disclaimer() ? '' : props.jsXSS} 79 | 80 | {disclaimer()} 81 | 82 | {disclaimer() ? '' : props.jqueryXSS} 83 | 84 | 85 | {`Instances of InnerHTML in scripts: ${props.innerHTML}`} 86 | 87 | 88 | 93 | {props.cookieExample ? ( 94 | 95 | ) : ( 96 | No Cookies Available 97 | )} 98 | 99 | {/* {props.cookieExample ? ( 100 | 101 | ) : ( 102 | No Cookies Available 103 | )} */} 104 | 105 | 106 | 107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /src/components/material/Dialog.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { withStyles } from '@material-ui/core/styles'; 4 | import Button from '@material-ui/core/Button'; 5 | import Dialog from '@material-ui/core/Dialog'; 6 | import MuiDialogTitle from '@material-ui/core/DialogTitle'; 7 | import MuiDialogContent from '@material-ui/core/DialogContent'; 8 | import MuiDialogActions from '@material-ui/core/DialogActions'; 9 | import IconButton from '@material-ui/core/IconButton'; 10 | import CloseIcon from '@material-ui/icons/Close'; 11 | import Typography from '@material-ui/core/Typography'; 12 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 13 | const styles = (theme) => ({ 14 | root: { 15 | margin: '2%', 16 | padding: theme.spacing(2), 17 | }, 18 | closeButton: { 19 | position: 'absolute', 20 | right: theme.spacing(1), 21 | top: theme.spacing(1), 22 | color: theme.palette.grey[500], 23 | }, 24 | }); 25 | const DialogTitle = withStyles(styles)((props) => { 26 | const { children, classes, onClose, ...other } = props; 27 | return ( 28 | 29 | {children} 30 | {onClose ? ( 31 | 32 | 33 | 34 | ) : null} 35 | 36 | ); 37 | }); 38 | const DialogContent = withStyles((theme) => ({ 39 | root: { 40 | padding: theme.spacing(2), 41 | }, 42 | }))(MuiDialogContent); 43 | const DialogActions = withStyles((theme) => ({ 44 | root: { 45 | margin: 0, 46 | padding: theme.spacing(1), 47 | }, 48 | }))(MuiDialogActions); 49 | export default function CustomizedDialogs(props) { 50 | const { jsXSS, jqueryTest, cookieTest, innerHTMLtest, url } = props.info; 51 | const disclaimer = () => { 52 | if (!url.includes('=')) { 53 | return ( 54 | 55 | * XSS attack tests require a query ('=') in the url * 56 | 57 | ); 58 | } 59 | }; 60 | const [open, setOpen] = React.useState(false); 61 | const handleClickOpen = () => { 62 | setOpen(true); 63 | }; 64 | const handleClose = () => { 65 | setOpen(false); 66 | }; 67 | 68 | let jQueryDialog = () => { 69 | if (!disclaimer()) { 70 | return (jqueryTest) ? 71 | This website is at risk for jQuery Reflected XSS attacks. Consider sanitizing inputs and search queries. 72 | : 73 | The website is not susceptible to jQuery Reflected XSS attacks. 74 | } 75 | 76 | } 77 | let jsXSSDialog = () => { 78 | if (!disclaimer()) { 79 | return (jsXSS) ? 80 | This website is at risk for Javascript Reflected XSS attacks. Consider sanitizing inputs and search queries. 81 | : 82 | The website is not susceptible to Javascript Reflected XSS attacks. 83 | } 84 | } 85 | 86 | const secureCookie = () => { 87 | if (cookieTest.every((cookie) => cookie.secure === false && cookie.httpOnly === false)) { 88 | return None of your cookies are secure. Consider making cookies with personal information or session information private by marking them as "secure" or "httpOnly". 89 | } 90 | else { 91 | return Some of your cookies are secure. Make sure that all cookies with personal information or session information are private. 92 | } 93 | } 94 | 95 | let innerHTMLDialog = () => { 96 | if (!innerHTMLtest) { 97 | return No instances of innerHTML found. 98 | } 99 | else { 100 | return {innerHTMLtest} instances of innerHTML detected. While this may be a result of your bundler, using this property to change input text can be a security risk. Make sure to sanitize user input before passing data from the front-end to the back-end. This will prevent stored or reflected XSS. 101 | } 102 | } 103 | 104 | return ( 105 |
106 | 111 | 112 | 113 | 114 | Defend Your Front-End 115 | 116 | 117 | 118 | {disclaimer()} 119 | {jsXSSDialog()} 120 | {jQueryDialog()} 121 | 122 | 123 | {secureCookie()} 124 | 125 | 126 | {innerHTMLDialog()} 127 | 128 | {/* */} 129 | 130 |
131 | ); 132 | } -------------------------------------------------------------------------------- /src/components/material/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import ListSubheader from '@material-ui/core/ListSubheader'; 4 | import List from '@material-ui/core/List'; 5 | import ListItem from '@material-ui/core/ListItem'; 6 | 7 | import ListItemText from '@material-ui/core/ListItemText'; 8 | 9 | const useStyles = makeStyles((theme) => ({ 10 | root: { 11 | width: '100%', 12 | maxWidth: 360, 13 | backgroundColor: theme.palette.background.paper, 14 | overflowY: 'hidden', 15 | }, 16 | nested: { 17 | paddingLeft: theme.spacing(4), 18 | }, 19 | })); 20 | 21 | export default function NestedList(props) { 22 | const classes = useStyles(); 23 | return ( 24 | 29 | 30 | Cookie Name:  31 | 32 | 33 | 34 | 35 | Domain:  36 | 37 | 38 | 39 | httpOnly:  40 | 41 | 42 | 43 | Secure:  44 | 45 | 46 | 47 | Same Party:  48 | 49 | 50 | 51 | Source Port:  52 | 53 | 54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/components/material/OpenSelect.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import InputLabel from '@material-ui/core/InputLabel'; 4 | import MenuItem from '@material-ui/core/MenuItem'; 5 | import FormControl from '@material-ui/core/FormControl'; 6 | import Select from '@material-ui/core/Select'; 7 | import Button from '@material-ui/core/Button'; 8 | 9 | import { ipcRenderer } from 'electron'; 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | button: { 13 | display: 'block', 14 | marginTop: theme.spacing(2), 15 | }, 16 | formControl: { 17 | margin: theme.spacing(1), 18 | minWidth: 120, 19 | }, 20 | })); 21 | 22 | export default function ControlledOpenSelect(props) { 23 | const classes = useStyles(); 24 | const [option, setOption] = React.useState(''); 25 | const [open, setOpen] = React.useState(false); 26 | const [label, setLabel] = React.useState(''); 27 | 28 | const options = props.options.map((el, i) => { 29 | return ( 30 | 31 | {el} 32 | 33 | ); 34 | }); 35 | 36 | //on load get values from local storage database 37 | useEffect(() => { 38 | ipcRenderer.send('load-data', props); 39 | ipcRenderer.once('data-reply', (event, arg) => { 40 | if ( 41 | typeof options[0].props.value === 'number' && 42 | options[0].props.value.toString().length === 1 43 | ) { 44 | setLabel(arg.historyLength); 45 | } else if (options[0].props.value === 'Regular Hacker Mode') { 46 | let cut = arg.theme.split(' '); 47 | setLabel(cut[0]); 48 | } else { 49 | setLabel(arg.fontSize); 50 | } 51 | }); 52 | }, []); 53 | 54 | //sends msg to update local storage upon change 55 | const handleChange = (event) => { 56 | setOption(event.target.value); 57 | 58 | ipcRenderer.once('asynchronous-reply', (event, arg) => {}); 59 | 60 | ipcRenderer.send('asynchronous-message', event.target); 61 | }; 62 | 63 | const handleClose = () => { 64 | setOpen(false); 65 | }; 66 | 67 | const handleOpen = () => { 68 | setOpen(true); 69 | }; 70 | 71 | return ( 72 |
73 | 74 | {label} 75 | 89 | 90 |
91 | ); 92 | } 93 | -------------------------------------------------------------------------------- /src/components/material/SideNav.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import clsx from "clsx"; 4 | import { makeStyles, useTheme } from "@material-ui/core/styles"; 5 | import Drawer from "@material-ui/core/Drawer"; 6 | import CssBaseline from "@material-ui/core/CssBaseline"; 7 | import AppBar from "@material-ui/core/AppBar"; 8 | import Toolbar from "@material-ui/core/Toolbar"; 9 | import List from "@material-ui/core/List"; 10 | import Typography from "@material-ui/core/Typography"; 11 | import Divider from "@material-ui/core/Divider"; 12 | import IconButton from "@material-ui/core/IconButton"; 13 | import MenuIcon from "@material-ui/icons/Menu"; 14 | import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; 15 | import ChevronRightIcon from "@material-ui/icons/ChevronRight"; 16 | import ListItem from "@material-ui/core/ListItem"; 17 | import ListItemIcon from "@material-ui/core/ListItemIcon"; 18 | import ListItemText from "@material-ui/core/ListItemText"; 19 | import Home from "@material-ui/icons/Home"; 20 | import { AccessTime } from "@material-ui/icons"; 21 | import SettingsApplicationsIcon from "@material-ui/icons/SettingsApplications"; 22 | import InfoIcon from "@material-ui/icons/Info"; 23 | import { default as Logo } from '../../../Logo.svg' 24 | 25 | import { useHistory } from "react-router-dom"; 26 | 27 | const drawerWidth = 240; 28 | 29 | const useStyles = makeStyles((theme) => ({ 30 | root: { 31 | display: "flex", 32 | }, 33 | appBar: { 34 | transition: theme.transitions.create(["margin", "width"], { 35 | easing: theme.transitions.easing.sharp, 36 | duration: theme.transitions.duration.leavingScreen, 37 | }), 38 | }, 39 | appBarShift: { 40 | width: `calc(100% - ${drawerWidth}px)`, 41 | // marginLeft: drawerWidth, 42 | transition: theme.transitions.create(["margin", "width"], { 43 | easing: theme.transitions.easing.easeOut, 44 | duration: theme.transitions.duration.enteringScreen, 45 | }), 46 | }, 47 | menuButton: { 48 | marginRight: theme.spacing(2), 49 | }, 50 | hide: { 51 | display: "none", 52 | }, 53 | drawer: { 54 | width: drawerWidth, 55 | flexShrink: 0, 56 | }, 57 | drawerPaper: { 58 | width: drawerWidth, 59 | }, 60 | drawerHeader: { 61 | display: "flex", 62 | alignItems: "center", 63 | 64 | // necessary for content to be below app bar 65 | ...theme.mixins.toolbar, 66 | justifyContent: "flex-end", 67 | }, 68 | content: { 69 | flexGrow: 1, 70 | transition: theme.transitions.create("margin", { 71 | easing: theme.transitions.easing.sharp, 72 | duration: theme.transitions.duration.leavingScreen, 73 | }), 74 | marginLeft: -drawerWidth, 75 | }, 76 | contentShift: { 77 | transition: theme.transitions.create("margin", { 78 | easing: theme.transitions.easing.easeOut, 79 | duration: theme.transitions.duration.enteringScreen, 80 | }), 81 | marginLeft: 0, 82 | }, 83 | })); 84 | 85 | export default function PersistentDrawerLeft(props) { 86 | const classes = useStyles(); 87 | const theme = useTheme(); 88 | const [open, setOpen] = React.useState(false); 89 | 90 | let history = useHistory(); 91 | 92 | function handleClickHome() { 93 | history.push("/home"); 94 | } 95 | 96 | function handleClickSettings() { 97 | history.push("/settings"); 98 | } 99 | 100 | function handleClickHistory() { 101 | history.push("/history"); 102 | } 103 | 104 | function handleClickAbout() { 105 | history.push("/about"); 106 | } 107 | 108 | const handleDrawerOpen = () => { 109 | setOpen(true); 110 | }; 111 | 112 | const handleDrawerClose = () => { 113 | setOpen(false); 114 | }; 115 | 116 | return ( 117 |
118 | 119 | 120 | 126 | 127 | 134 | 135 | 136 |
137 |
138 | 139 |
140 | 141 | 150 |
151 | 152 | {theme.direction === "ltr" ? ( 153 | 154 | ) : ( 155 | 156 | )} 157 | 158 |
159 | 160 | 161 | {["Home", "History"].map((text, index) => ( 162 | 167 | 168 | {index % 2 === 0 ? : } 169 | 170 | 171 | 172 | ))} 173 | 174 | 175 | 176 | {["Settings", "About"].map((text, index) => ( 177 | 182 | 183 | {index % 2 === 0 ? : } 184 | 185 | 186 | 187 | ))} 188 | 189 |
190 |
195 |
196 |
197 |
198 | ); 199 | } 200 | -------------------------------------------------------------------------------- /src/components/material/spinner/Spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { usePromiseTracker } from 'react-promise-tracker'; 3 | import Loader from 'react-loader-spinner'; 4 | import './spinner.css'; 5 | 6 | export default function Spinner(props) { 7 | const { promiseInProgress } = usePromiseTracker(); 8 | 9 | return ( 10 | promiseInProgress && ( 11 |
12 | 19 |
20 | ) 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/material/spinner/index.js: -------------------------------------------------------------------------------- 1 | export * from './Spinner'; 2 | -------------------------------------------------------------------------------- /src/components/material/spinner/spinner.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | margin-top: 20%; 3 | margin-bottom: 20%; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/pages/About.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { ipcRenderer } from 'electron'; 3 | import { CssBaseline, Paper } from '@material-ui/core'; 4 | import PermanentDrawerLeft from '../material/SideNav'; 5 | import { createTheme, ThemeProvider } from '@material-ui/core/styles'; 6 | import { Typography } from '@material-ui/core'; 7 | import { default as Logo } from '../../../Logo.svg' 8 | 9 | 10 | function About() { 11 | const [label, setLabel] = useState({}); 12 | 13 | useEffect(() => { 14 | ipcRenderer.send('load-data'); 15 | ipcRenderer.once('data-reply', (event, arg) => { 16 | setLabel(arg); 17 | }); 18 | }, []); 19 | 20 | const styles = { 21 | listStyleType: 'none', 22 | }; 23 | 24 | let theme; 25 | 26 | if (label.theme === 'Regular Hacker Mode') theme = createTheme(label.light); 27 | if (label.theme === 'Dark XSS Mode') theme = createTheme(label.dark); 28 | if (label.theme === 'Blue DOS Mode') theme = createTheme(label.blue); 29 | if (label.theme === 'Purple SQL Injection Mode') theme = createTheme(label.purple); 30 | if (label.theme === 'Green Slow Loris Mode') theme = createTheme(label.green); 31 | 32 | return ( 33 | 34 | 35 |
36 |
37 | 38 | About 39 | 40 |
41 | 42 | 43 | Version 44 | 45 | 46 | 1.0.0 47 | 48 |

49 | 50 | How it Works? 51 | 52 | 53 | Input a URL on the home page and see security readings once the test has loaded. 54 | 55 |

56 | 57 | Who it's for? 58 | 59 | 60 | This app is for any developer looking to test their front end application for 61 | Cross-Site-Scripting or Cookie vulnerabilities. 62 | 63 |

64 | 65 | Disclaimer 66 | 67 | 68 | Although the tests are non-invasive, it is suggested to only input URLs that the user is authorized to pentest. 69 | 70 |
71 |
{ 72 | (label.theme === 'Regular Hacker Mode') ? 73 | : 74 | 75 | } 76 |
77 | {/*
*/} 78 | 79 |
80 |
81 | ); 82 | } 83 | 84 | export default About; 85 | -------------------------------------------------------------------------------- /src/components/pages/History.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { ipcRenderer } from 'electron'; 3 | import { CssBaseline, Paper } from '@material-ui/core'; 4 | import PermanentDrawerLeft from '../material/SideNav'; 5 | import { createTheme, ThemeProvider } from '@material-ui/core/styles'; 6 | import Button from '@material-ui/core/Button'; 7 | import { makeStyles } from '@material-ui/core/styles'; 8 | import ControlledOpenSelect from '../material/OpenSelect'; 9 | import { Typography } from '@material-ui/core'; 10 | import Card from '../material/Card'; 11 | import CustomizedDialogs from '../material/Dialog'; 12 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 13 | import { default as Logo } from '../../../Logo.svg' 14 | 15 | const useStyles = makeStyles((theme) => ({ 16 | margin: { 17 | margin: theme.spacing(1), 18 | }, 19 | extendedIcon: { 20 | marginRight: theme.spacing(1), 21 | }, 22 | })); 23 | 24 | function History() { 25 | const [label, setLabel] = useState({}); 26 | const [history, setHistory] = useState([]); 27 | const [historyLength, setHistoryLength] = useState(3); 28 | 29 | useEffect(() => { 30 | let isFetched = true; 31 | try { 32 | ipcRenderer.send('load-data'); 33 | ipcRenderer.once('data-reply', (event, arg) => { 34 | setLabel(arg); 35 | 36 | if (isFetched) setHistory(arg.history); 37 | if (arg.historyLength) setHistoryLength(arg.historyLength); 38 | }); 39 | } catch (e) { 40 | console.log(e); 41 | } 42 | 43 | // cancel async otherwise to prevent memory leak 44 | return () => (isFetched = false); 45 | }, []); 46 | 47 | const classes = useStyles(); 48 | 49 | let theme; 50 | 51 | if (label.theme === 'Regular Hacker Mode') theme = createTheme(label.light); 52 | if (label.theme === 'Dark XSS Mode') theme = createTheme(label.dark); 53 | if (label.theme === 'Blue DOS Mode') theme = createTheme(label.blue); 54 | if (label.theme === 'Purple SQL Injection Mode') theme = createTheme(label.purple); 55 | if (label.theme === 'Green Slow Loris Mode') theme = createTheme(label.green); 56 | 57 | const clearItem = (index) => { 58 | ipcRenderer.send('clearItem', index); 59 | ipcRenderer.once('itemCleared', (event, arg) => { 60 | setHistory(arg); 61 | }); 62 | }; 63 | const pastStats = history.slice(0, historyLength).map((el, i) => { 64 | return ( 65 |
66 |
67 | 82 |
83 |
84 | 85 | 95 |
96 |
97 | ); 98 | }); 99 | 100 | const clearHistory = () => { 101 | ipcRenderer.send('clearHistory'); 102 | ipcRenderer.once('historyCleared', (event, arg) => { 103 | setHistory(arg); 104 | }); 105 | }; 106 | 107 | const historyLengths = [2, 4, 6, 8]; 108 | 109 | const clicked = () => { 110 | ipcRenderer.send('getHistoryLength'); 111 | ipcRenderer.once('length', (event, arg) => { 112 | setHistoryLength(arg); 113 | }); 114 | }; 115 | 116 | return ( 117 | 118 | 119 |
120 |
121 | 122 | History 123 | 124 |
125 |
126 | 127 | 128 | 131 | 140 | {/* */} 143 | 144 | 145 |
146 |
    {pastStats}
147 |
148 |
{ 149 | (label.theme === 'Regular Hacker Mode') ? 150 | : 151 | 152 | } 153 |
154 | {/*
*/} 155 |
156 | ); 157 | } 158 | 159 | export default History; 160 | -------------------------------------------------------------------------------- /src/components/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import PermanentDrawerLeft from '../material/SideNav'; 4 | import Button from '@material-ui/core/Button'; 5 | import { ipcRenderer } from 'electron'; 6 | import { createTheme, ThemeProvider } from '@material-ui/core/styles'; 7 | import Paper from '@material-ui/core/Paper'; 8 | import { CssBaseline } from '@material-ui/core'; 9 | import axios from 'axios'; 10 | import Typography from '@material-ui/core/Typography'; 11 | import { TextField } from '@material-ui/core'; 12 | import Card from '../material/Card'; 13 | import { trackPromise, usePromiseTracker } from 'react-promise-tracker'; 14 | import Spinner from '../material/spinner/Spinner'; 15 | import { default as Logo } from '../../../Logo.svg' 16 | import CustomizedDialogs from '../material/Dialog'; 17 | 18 | 19 | const useStyles = makeStyles((theme) => ({ 20 | margin: { 21 | margin: theme.spacing(1), 22 | }, 23 | extendedIcon: { 24 | marginRight: theme.spacing(1), 25 | }, 26 | })); 27 | 28 | function Home() { 29 | const [label, setLabel] = useState({}); 30 | const [testResults, setTestResults] = useState(''); 31 | const [isVisible, setVisible] = useState(false); 32 | 33 | useEffect(() => { 34 | ipcRenderer.send('load-data'); 35 | ipcRenderer.once('data-reply', (event, arg) => { 36 | setLabel(arg); 37 | }); 38 | }, []); 39 | 40 | let theme; 41 | 42 | if (label.theme === 'Regular Hacker Mode') theme = createTheme(label.light); 43 | if (label.theme === 'Dark XSS Mode') theme = createTheme(label.dark); 44 | if (label.theme === 'Blue DOS Mode') theme = createTheme(label.blue); 45 | if (label.theme === 'Purple SQL Injection Mode') theme = createTheme(label.purple); 46 | if (label.theme === 'Green Slow Loris Mode') theme = createTheme(label.green); 47 | 48 | const classes = useStyles(); 49 | 50 | const sendURL = () => { 51 | let link = document.getElementsByName('url')[0].value; 52 | 53 | setTestResults(''); 54 | 55 | let userObject = { 56 | url: link, 57 | }; 58 | 59 | let jsXssResult, jqueryResult, cookieResult; 60 | 61 | const date = new Date(); 62 | 63 | let testStats = { 64 | url: link, 65 | jsXSS: null, 66 | jqueryTest: null, 67 | cookieTest: null, 68 | innerHTMLtest: null, 69 | currentTime: date.toUTCString(), 70 | }; 71 | 72 | testStats.innerHTMLtest = null; 73 | 74 | const fetches = () => { 75 | setVisible(true); 76 | axios 77 | .post('http://localhost:5000/javascriptXSS', userObject) 78 | .then((res) => { 79 | console.log(res.data); 80 | testStats.jsXSS = res.data; 81 | jsXssResult = res.data; 82 | }) 83 | 84 | .then(() => { 85 | axios 86 | .post('http://localhost:5000/cookieTester', userObject) 87 | .then((res) => { 88 | testStats.cookieTest = res.data; 89 | cookieResult = res.data; 90 | }) 91 | .then(() => { 92 | axios 93 | .post('http://localhost:5000/innerHTML', userObject) 94 | .then((res) => { 95 | testStats.innerHTMLtest = res.data; 96 | }) 97 | .then(() => { 98 | ipcRenderer.send('url', testStats); 99 | ipcRenderer.once('testOutput', (event, arg) => { 100 | arg.url = link 101 | arg.jsXSS = testStats.jsXSS 102 | arg.jqueryTest = testStats.jqueryTest 103 | setVisible(false); 104 | setTestResults( 105 |
106 | 122 | 123 |
124 | ); 125 | }); 126 | }) 127 | 128 | .catch((error) => { 129 | console.log(error); 130 | }); 131 | }) 132 | 133 | .catch((error) => { 134 | console.log(error); 135 | }); 136 | }) 137 | 138 | .then(() => { 139 | axios 140 | .post('http://localhost:5000/jqueryXSS', userObject) 141 | .then((res) => { 142 | testStats.jqueryTest = res.data; 143 | jqueryResult = res.data; 144 | }) 145 | 146 | .catch((error) => { 147 | console.log(error); 148 | }); 149 | }); 150 | }; 151 | 152 | //track promise, invoke spinner 153 | trackPromise(fetches()); 154 | }; 155 | 156 | const disclaimer = () => { 157 | return document.getElementsByName('url')[0].value.includes('q=') 158 | ? '' 159 | : 'For XSS testing please input a url containg a q= query'; 160 | }; 161 | 162 | return ( 163 | 164 | 165 | 166 |
167 | 168 |
169 |
170 | 171 | Scan Link 172 | 173 |
174 |

175 |
176 | 177 |
178 | 185 | 194 | 195 |
196 |
197 |

198 |
199 | 200 | Results 201 | 202 |
203 |
204 | 209 | {testResults} 210 | 211 | 212 |
213 |
214 |
215 | {/* */} 216 |
{ 217 | (label.theme === 'Regular Hacker Mode') ? 218 | : 219 | 220 | } 221 | {/*
*/} 222 |
223 | 224 |
225 |
226 | ); 227 | } 228 | 229 | export default Home; 230 | -------------------------------------------------------------------------------- /src/components/pages/Settings.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import PermanentDrawerLeft from '../material/SideNav'; 3 | import ControlledOpenSelect from '../material/OpenSelect'; 4 | import { Button } from '@material-ui/core'; 5 | import { ipcRenderer } from 'electron'; 6 | import { createTheme, ThemeProvider } from '@material-ui/core/styles'; 7 | import Paper from '@material-ui/core/Paper'; 8 | import Typography from '@material-ui/core/Typography'; 9 | import { CssBaseline } from '@material-ui/core'; 10 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 11 | import { default as Logo } from '../../../Logo.svg' 12 | 13 | function Settings() { 14 | const [label, setLabel] = useState({}); 15 | const [dummyState, setDummy] = useState(0); 16 | 17 | useEffect(() => { 18 | let isFetched = true; 19 | try { 20 | ipcRenderer.send('load-data'); 21 | ipcRenderer.on('data-reply', (event, arg) => { 22 | if (isFetched) setLabel(arg); 23 | }); 24 | } catch (e) { 25 | console.log(e); 26 | } 27 | 28 | // cancel async otherwise to prevent memory leak 29 | return () => (isFetched = false); 30 | }, []); 31 | 32 | let theme; 33 | 34 | if (label.theme === 'Regular Hacker Mode') theme = createTheme(label.light); 35 | if (label.theme === 'Dark XSS Mode') theme = createTheme(label.dark); 36 | if (label.theme === 'Blue DOS Mode') theme = createTheme(label.blue); 37 | if (label.theme === 'Purple SQL Injection Mode') theme = createTheme(label.purple); 38 | if (label.theme === 'Green Slow Loris Mode') theme = createTheme(label.green); 39 | 40 | const modes = [ 41 | 'Regular Hacker Mode', 42 | 'Dark XSS Mode', 43 | 'Blue DOS Mode', 44 | 'Purple SQL Injection Mode', 45 | 'Green Slow Loris Mode', 46 | ]; 47 | const fontSizes = ['12px', '16px', '20px', '24px']; 48 | 49 | const clicked = () => { 50 | ipcRenderer.send('load-data'); 51 | ipcRenderer.once('data-reply', (event, arg) => { 52 | setLabel(arg); 53 | }); 54 | 55 | setDummy(dummyState + 1); 56 | }; 57 | 58 | return ( 59 | 60 | 61 |
62 |
63 | Settings 64 |
65 | 66 | Color Themes 67 | 68 |

69 | Text Size 70 |

71 | 72 |

73 | 76 |
77 | {/* */} 78 |
{ 79 | (label.theme === 'Regular Hacker Mode') ? 80 | : 81 | 82 | } 83 | {/*
*/} 84 |
85 | 86 |
87 |
88 | ); 89 | } 90 | 91 | export default Settings; 92 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | import { BrowserRouter } from 'react-router-dom'; 6 | 7 | // Since we are using HtmlWebpackPlugin WITHOUT a template, we should create our own root node in the body element before rendering into it 8 | let root = document.createElement('div'); 9 | 10 | root.id = 'root'; 11 | document.body.appendChild(root); 12 | 13 | // Now we can render our application into it 14 | render( 15 | 16 | 17 | , 18 | document.getElementById('root') 19 | ); 20 | -------------------------------------------------------------------------------- /webpack.build.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | 4 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | 7 | // Any directories you will be adding code/files into, need to be added to this array so webpack will pick them up 8 | const defaultInclude = path.resolve(__dirname, "src"); 9 | 10 | module.exports = { 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.css$/, 15 | use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"], 16 | include: defaultInclude, 17 | }, 18 | { 19 | test: /\.(js|jsx)?$/, 20 | use: [{ loader: "babel-loader" }], 21 | include: defaultInclude, 22 | }, 23 | { 24 | test: /\.(jpe?g|png|gif)$/, 25 | use: [{ loader: "file-loader?name=img/[name]__[hash:base64:5].[ext]" }], 26 | include: defaultInclude, 27 | }, 28 | { 29 | test: /\.(eot|svg|ttf|woff|woff2)$/, 30 | use: [ 31 | { loader: "file-loader?name=font/[name]__[hash:base64:5].[ext]" }, 32 | ], 33 | include: defaultInclude, 34 | }, 35 | { 36 | test: /\.svg$/, 37 | use: ['@svgr/webpack'], 38 | } 39 | ], 40 | }, 41 | target: "electron-renderer", 42 | plugins: [ 43 | new HtmlWebpackPlugin(), 44 | 45 | new MiniCssExtractPlugin({ 46 | // Options similar to the same options in webpackOptions.output 47 | // both options are optional 48 | filename: "bundle.css", 49 | chunkFilename: "[id].css", 50 | }), 51 | new webpack.DefinePlugin({ 52 | "process.env.NODE_ENV": JSON.stringify("production"), 53 | }), 54 | // new MinifyPlugin() 55 | ], 56 | stats: { 57 | colors: true, 58 | children: false, 59 | chunks: false, 60 | modules: false, 61 | }, 62 | node: { 63 | __dirname: false, 64 | __filename: false, 65 | }, 66 | optimization: { 67 | minimize: true, 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { spawn } = require('child_process'); 5 | 6 | // Any directories you will be adding code/files into, need to be added to this array so webpack will pick them up 7 | const defaultInclude = path.resolve(__dirname, 'src'); 8 | 9 | module.exports = { 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.s?css$/, 14 | use: [ 15 | { loader: 'style-loader' }, 16 | { loader: 'css-loader' }, 17 | { loader: 'postcss-loader' }, 18 | ], 19 | include: defaultInclude, 20 | }, 21 | { 22 | test: /\.jsx?$/, 23 | use: [{ loader: 'babel-loader' }], 24 | include: defaultInclude, 25 | }, 26 | { 27 | test: /\.(jpe?g|png|gif)$/, 28 | use: [{ loader: 'file-loader?name=img/[name]__[hash:base64:5].[ext]' }], 29 | include: defaultInclude, 30 | }, 31 | { 32 | test: /\.svg$/, 33 | use: ['@svgr/webpack'], 34 | } 35 | ], 36 | }, 37 | target: 'electron-renderer', 38 | plugins: [ 39 | new HtmlWebpackPlugin(), 40 | new webpack.DefinePlugin({ 41 | 'process.env.NODE_ENV': JSON.stringify('development'), 42 | }), 43 | ], 44 | devtool: 'cheap-source-map', 45 | devServer: { 46 | contentBase: path.resolve(__dirname, 'dist'), 47 | stats: { 48 | colors: true, 49 | chunks: false, 50 | children: false, 51 | }, 52 | before() { 53 | spawn('electron', ['.'], { shell: true, env: process.env, stdio: 'inherit' }) 54 | .on('close', (code) => process.exit(0)) 55 | .on('error', (spawnError) => console.error(spawnError)); 56 | }, 57 | }, 58 | }; 59 | --------------------------------------------------------------------------------