├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jscsrc ├── .jshintrc ├── README.md ├── attacker-app ├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jscsrc ├── .jshintrc ├── README.md ├── bower.json ├── gulp.config.js ├── gulp.png ├── gulpfile.js ├── karma.conf.js ├── package.json └── src │ ├── client │ ├── app │ │ ├── app.module.js │ │ ├── blocks │ │ │ ├── exception │ │ │ │ ├── exception-handler.provider.js │ │ │ │ ├── exception-handler.provider.spec.js │ │ │ │ ├── exception.js │ │ │ │ └── exception.module.js │ │ │ ├── logger │ │ │ │ ├── logger.js │ │ │ │ └── logger.module.js │ │ │ └── router │ │ │ │ ├── router-helper.provider.js │ │ │ │ └── router.module.js │ │ ├── clickjacking-attack │ │ │ ├── clickjacking-attack.controller.js │ │ │ ├── clickjacking-attack.controller.spec.js │ │ │ ├── clickjacking-attack.html │ │ │ ├── clickjacking-attack.module.js │ │ │ ├── clickjacking-attack.route.js │ │ │ └── clickjacking-attack.route.spec.js │ │ ├── core │ │ │ ├── 404.html │ │ │ ├── config.js │ │ │ ├── constants.js │ │ │ ├── core.module.js │ │ │ ├── core.route.js │ │ │ ├── core.route.spec.js │ │ │ └── dataservice.js │ │ ├── csrf-attack │ │ │ ├── csrf-attack.controller.js │ │ │ ├── csrf-attack.controller.spec.js │ │ │ ├── csrf-attack.html │ │ │ ├── csrf-attack.module.js │ │ │ ├── csrf-attack.route.js │ │ │ └── csrf-attack.route.spec.js │ │ ├── dashboard │ │ │ ├── dashboard.controller.js │ │ │ ├── dashboard.controller.spec.js │ │ │ ├── dashboard.html │ │ │ ├── dashboard.module.js │ │ │ ├── dashboard.route.js │ │ │ └── dashboard.route.spec.js │ │ ├── layout │ │ │ ├── ht-sidebar.directive.js │ │ │ ├── ht-sidebar.directive.spec.js │ │ │ ├── ht-top-nav.directive.js │ │ │ ├── ht-top-nav.html │ │ │ ├── layout.module.js │ │ │ ├── shell.controller.js │ │ │ ├── shell.controller.spec.js │ │ │ ├── shell.html │ │ │ ├── sidebar.controller.js │ │ │ ├── sidebar.controller.spec.js │ │ │ └── sidebar.html │ │ └── widgets │ │ │ ├── ht-img-person.directive.js │ │ │ ├── ht-widget-header.directive.js │ │ │ ├── widget-header.html │ │ │ └── widgets.module.js │ ├── images │ │ ├── AngularJS-small.png │ │ ├── busy.gif │ │ ├── gulp-tiny.png │ │ └── icon.png │ ├── index.html │ ├── specs.html │ ├── styles │ │ └── styles.less │ └── test-helpers │ │ ├── bind-polyfill.js │ │ └── mock-data.js │ └── server │ ├── app.js │ ├── data.js │ ├── favicon.ico │ ├── routes.js │ └── utils │ └── 404.js ├── bower.json ├── gulp.config.js ├── gulp.png ├── gulpfile.js ├── karma.conf.js ├── package.json └── src ├── client ├── app │ ├── app.module.js │ ├── blocks │ │ ├── exception │ │ │ ├── exception-handler.provider.js │ │ │ ├── exception-handler.provider.spec.js │ │ │ ├── exception.js │ │ │ └── exception.module.js │ │ ├── logger │ │ │ ├── logger.js │ │ │ └── logger.module.js │ │ └── router │ │ │ ├── router-helper.provider.js │ │ │ └── router.module.js │ ├── clickjacking │ │ ├── clickjacking.controller.js │ │ ├── clickjacking.controller.spec.js │ │ ├── clickjacking.html │ │ ├── clickjacking.module.js │ │ ├── clickjacking.route.js │ │ └── clickjacking.route.spec.js │ ├── core │ │ ├── 404.html │ │ ├── config.js │ │ ├── constants.js │ │ ├── core.module.js │ │ ├── core.route.js │ │ ├── core.route.spec.js │ │ ├── dataservice.js │ │ └── user.service.js │ ├── csrf │ │ ├── csrf.controller.js │ │ ├── csrf.controller.spec.js │ │ ├── csrf.html │ │ ├── csrf.module.js │ │ ├── csrf.route.js │ │ └── csrf.route.spec.js │ ├── dashboard │ │ ├── dashboard.controller.js │ │ ├── dashboard.controller.spec.js │ │ ├── dashboard.html │ │ ├── dashboard.module.js │ │ ├── dashboard.route.js │ │ └── dashboard.route.spec.js │ ├── layout │ │ ├── ht-sidebar.directive.js │ │ ├── ht-sidebar.directive.spec.js │ │ ├── ht-top-nav.directive.js │ │ ├── ht-top-nav.html │ │ ├── layout.module.js │ │ ├── shell.controller.js │ │ ├── shell.controller.spec.js │ │ ├── shell.html │ │ ├── sidebar.controller.js │ │ ├── sidebar.controller.spec.js │ │ └── sidebar.html │ ├── widgets │ │ ├── ht-img-person.directive.js │ │ ├── ht-widget-header.directive.js │ │ ├── widget-header.html │ │ └── widgets.module.js │ └── xss-search │ │ ├── xss-search.controller.js │ │ ├── xss-search.controller.spec.js │ │ ├── xss-search.html │ │ ├── xss-search.module.js │ │ ├── xss-search.route.js │ │ └── xss-search.route.spec.js ├── images │ ├── AngularJS-small.png │ ├── busy.gif │ ├── gulp-tiny.png │ └── icon.png ├── index.html ├── specs.html ├── styles │ └── styles.less └── test-helpers │ ├── bind-polyfill.js │ └── mock-data.js └── server ├── app.js ├── data.js ├── favicon.ico ├── routes.js └── utils └── 404.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "scripts": { 4 | "postinstall": "gulp wiredep" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 3 | node_modules/ 4 | bower_components/ 5 | 6 | # other 7 | .tmp 8 | report/ 9 | build/ -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": ["node_modules/**", "bower_components/**"], 3 | 4 | "requireCurlyBraces": [ 5 | "if", 6 | "else", 7 | "for", 8 | "while", 9 | "do", 10 | "try", 11 | "catch" 12 | ], 13 | "requireOperatorBeforeLineBreak": true, 14 | "requireCamelCaseOrUpperCaseIdentifiers": true, 15 | "maximumLineLength": { 16 | "value": 100, 17 | "allowComments": true, 18 | "allowRegex": true 19 | }, 20 | "validateIndentation": 4, 21 | "validateQuoteMarks": "'", 22 | 23 | "disallowMultipleLineStrings": true, 24 | "disallowMixedSpacesAndTabs": true, 25 | "disallowTrailingWhitespace": true, 26 | "disallowSpaceAfterPrefixUnaryOperators": true, 27 | "disallowMultipleVarDecl": null, 28 | 29 | "requireSpaceAfterKeywords": [ 30 | "if", 31 | "else", 32 | "for", 33 | "while", 34 | "do", 35 | "switch", 36 | "return", 37 | "try", 38 | "catch" 39 | ], 40 | "requireSpaceBeforeBinaryOperators": [ 41 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 42 | "&=", "|=", "^=", "+=", 43 | 44 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 45 | "|", "^", "&&", "||", "===", "==", ">=", 46 | "<=", "<", ">", "!=", "!==" 47 | ], 48 | "requireSpaceAfterBinaryOperators": true, 49 | "requireSpacesInConditionalExpression": true, 50 | "requireSpaceBeforeBlockStatements": true, 51 | "requireLineFeedAtFileEnd": true, 52 | "disallowSpacesInsideObjectBrackets": "all", 53 | "disallowSpacesInsideArrayBrackets": "all", 54 | "disallowSpacesInsideParentheses": true, 55 | 56 | "jsDoc": { 57 | "checkAnnotations": true, 58 | "checkParamNames": true, 59 | "requireParamTypes": true, 60 | "checkReturnTypes": true, 61 | "checkTypes": true 62 | }, 63 | 64 | "disallowMultipleLineBreaks": true, 65 | 66 | "disallowCommaBeforeLineBreak": null, 67 | "disallowDanglingUnderscores": null, 68 | "disallowEmptyBlocks": null, 69 | "disallowTrailingComma": null, 70 | "requireCommaBeforeLineBreak": null, 71 | "requireDotNotation": null, 72 | "requireMultipleVarDecl": null, 73 | "requireParenthesesAroundIIFE": true 74 | } 75 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": "nofunc", 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "single", 19 | "undef": true, 20 | "unused": false, 21 | "strict": false, 22 | "maxparams": 10, 23 | "maxdepth": 5, 24 | "maxstatements": 40, 25 | "maxcomplexity": 8, 26 | "maxlen": 120, 27 | 28 | "asi": false, 29 | "boss": false, 30 | "debug": false, 31 | "eqnull": true, 32 | "esnext": false, 33 | "evil": false, 34 | "expr": false, 35 | "funcscope": false, 36 | "globalstrict": false, 37 | "iterator": false, 38 | "lastsemic": false, 39 | "laxbreak": false, 40 | "laxcomma": false, 41 | "loopfunc": true, 42 | "maxerr": 50, 43 | "moz": false, 44 | "multistr": false, 45 | "notypeof": false, 46 | "proto": false, 47 | "scripturl": false, 48 | "shadow": false, 49 | "sub": true, 50 | "supernew": false, 51 | "validthis": false, 52 | "noyield": false, 53 | 54 | "browser": true, 55 | "node": true, 56 | 57 | "globals": { 58 | "angular": false 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulnerable-app & attacker-app 2 | There are two applications within this repository that were generated from the HotTowel Angular generator. The main one is the `vulnerable-app` which is found in the `/src` folder. This application was built intentionally built out with vulnerabilities to easily demonstrate how they are performed by an attacker. The secondary application is the `attacker-app` found in the `/attacker-app` folder and it was built out to assist in demonstrating an attacker's website that is exploiting the vulnerabilities in the `vulnerable-app`. 3 | 4 | ## Requirements 5 | 1. Node.js v4.2.x 6 | 2. NPM v3.10.x 7 | 8 | > Straying from these versions may result in unanticipated behavior and it cannot be guaranteed the app will produce the expected results. 9 | 10 | ## How to Run Both Apps 11 | 1. Open your terminal and `cd` to the root folder for this repository 12 | 2. Run `gulp serve-dev` to spin up the `vulnerable-app` 13 | 3. You should see your browser open up a new tab to the following URL: [http://localhost:3000](http://localhost:3000) 14 | 4. Open a new terminal window or tab and `cd` to the `/attacker-app` folder from the root location of this repository 15 | 5. Run `gulp serve-dev` 16 | 6. You should see your browser open up another new tab to the following URL: [http://localhost:3002](http://localhost:3002) 17 | 18 | ## How to Test 19 | 20 | ### XSS 21 | The following steps will demonstrate a simple example of being able to escape the context of where the search input text is printed on screen and used to execute an injectable script that the browser will execute. 22 | 23 | 1. In the tab that's running the `vulnerable-app`, click on the option `XSS-Search` in the navigation bar 24 | 2. In the "Search" field enter the following text: `` 25 | 3. Click the "Submit" button 26 | 4. You should see an alert message pop up on your screen with the message "Malicious Script!" 27 | 28 | ### CSRF 29 | The following steps will demonstrate a simple example of being able to submit requests on behalf of the logged in user within the vulnerable-app, but executed from the `attacker-app`. 30 | 31 | 1. In the tab that's running the `vulnerable-app`, click on the option `CSRF` in the navigation bar and take note of the "User Profile" section within the view 32 | > By default, the user's "First Name" should show the value of `Jim` and the "Last Name" as the value of `Bob` 33 | 34 | 2. In the tab that's running the `attacker-app`, click on the option `CSRF-Attack` in the navigation bar. This will immediately execute the CSRF attack and display the forged POST data 35 | 3. Go back to the tab that's running the `vulnerable-app` and make sure you're still in the `CSRF` view 36 | 4. Click the "Get Latest User Profile" button and you should see that the user's profile was changed due to the CSRF attack 37 | 38 | > The user's "First Name" should show the value of `Evil` and the "Last Name" as the value of `Hacker` now 39 | 40 | ### Clickjacking 41 | The following steps will demonstrate a simple example of clickjacking by tricking the user of the `vulnerable-app` to click a seemingly harmless button in the `attacker-app` that actually executes an action in the `vulnerable-app`. 42 | 43 | 1. In the tab that's running the `attacker-app`, click on the option `Clickjacking-Attack` 44 | > You should be able to see that the `vulnerable-app` is loaded in the view, but with a low opacity 45 | 46 | 2. Open the developer tools for the browser you're using and view the console 47 | 3. Click the "Click to see awesome dog backflips!" button 48 | 49 | > You should see a message in the console with the following text: "The profile was successfully deleted!" 50 | 51 | This example demonstrates that while the user thinks they're clicking on a button that will show them "awesome dog backflips", they're actually clicking on the "Delete Sensitive Information!" button found in the `vulnerable-app`. This is accomplished because the `attacker-app` can load the `vulnerable-app` in an `iframe` html element, style the iframe so it's not visible at all (in this case it is somewhat visible for demonstration purposes) and actually a "layer" deep from other html elements within the view, and place "clickbait" type elements on top of the iframe and over the areas the attacker wants the user to click within the iframe instead. 52 | 53 | ## References/Further Reading 54 | 1. [OWASP](https://www.owasp.org/) 55 | 1. [Cross-site Scripting Defense Cheat Sheet][1] 56 | 1. [Cross-site Request Forgery Defense Cheat Sheet][2] 57 | 1. [Clickjacking Defense Cheat Sheet](https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet) 58 | 2. [HTML5Rocks - CSP](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) 59 | 3. [Angular $sanitize](https://docs.angularjs.org/api/ngSanitize/service/$sanitize) 60 | 4. [Angular $sce](https://docs.angularjs.org/api/ng/service/$sce) 61 | 5. [xss-filters](https://www.npmjs.com/package/xss-filters) 62 | 6. [lusca](https://www.npmjs.com/package/lusca) 63 | 64 | [1]: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet 65 | [2]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet 66 | 67 | ------- 68 | 69 | **Generated from HotTowel Angular** 70 | 71 | >*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* 72 | 73 | >More details about the styles and patterns used in this app can be found in my [Angular Style Guide](https://github.com/johnpapa/angularjs-styleguide) and my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at [Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) and working in teams. 74 | 75 | ## Prerequisites 76 | 77 | 1. Install [Node.js](http://nodejs.org) 78 | - on OSX use [homebrew](http://brew.sh) `brew install node` 79 | - on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs` 80 | 81 | 2. Install Yeoman `npm install -g yo` 82 | 83 | 3. Install these NPM packages globally 84 | 85 | ```bash 86 | npm install -g bower gulp nodemon 87 | ``` 88 | 89 | >Refer to these [instructions on how to not require sudo](https://github.com/sindresorhus/guides/blob/master/npm-global-without-sudo.md) 90 | 91 | ## Running HotTowel 92 | 93 | ### Linting 94 | - Run code analysis using `gulp vet`. This runs jshint, jscs, and plato. 95 | 96 | ### Tests 97 | - Run the unit tests using `gulp test` (via karma, mocha, sinon). 98 | 99 | ### Running in dev mode 100 | - Run the project with `gulp serve-dev` 101 | 102 | - opens it in a browser and updates the browser with any files changes. 103 | 104 | ### Building the project 105 | - Build the optimized project using `gulp build` 106 | - This create the optimized code for the project and puts it in the build folder 107 | 108 | ### Running the optimized code 109 | - Run the optimize project from the build folder with `gulp serve-build` 110 | 111 | ## Exploring HotTowel 112 | HotTowel Angular starter project 113 | 114 | ### Structure 115 | The structure also contains a gulpfile.js and a server folder. The server is there just so we can serve the app using node. Feel free to use any server you wish. 116 | 117 | /src 118 | /client 119 | /app 120 | /content 121 | 122 | ### Installing Packages 123 | When you generate the project it should run these commands, but if you notice missing packages, run these again: 124 | 125 | - `npm install` 126 | - `bower install` 127 | 128 | ### The Modules 129 | The app has 4 feature modules and depends on a series of external modules and custom but cross-app modules 130 | 131 | ``` 132 | app --> [ 133 | app.admin --> [ 134 | app.core, 135 | app.widgets 136 | ], 137 | app.dashboard --> [ 138 | app.core, 139 | app.widgets 140 | ], 141 | app.layout --> [ 142 | app.core 143 | ], 144 | app.widgets, 145 | app.core --> [ 146 | ngAnimate, 147 | ngSanitize, 148 | ui.router, 149 | blocks.exception, 150 | blocks.logger, 151 | blocks.router 152 | ] 153 | ] 154 | ``` 155 | 156 | #### core Module 157 | Core modules are ones that are shared throughout the entire application and may be customized for the specific application. Example might be common data services. 158 | 159 | This is an aggregator of modules that the application will need. The `core` module takes the blocks, common, and Angular sub-modules as dependencies. 160 | 161 | #### blocks Modules 162 | Block modules are reusable blocks of code that can be used across projects simply by including them as dependencies. 163 | 164 | ##### blocks.logger Module 165 | The `blocks.logger` module handles logging across the Angular app. 166 | 167 | ##### blocks.exception Module 168 | The `blocks.exception` module handles exceptions across the Angular app. 169 | 170 | It depends on the `blocks.logger` module, because the implementation logs the exceptions. 171 | 172 | ##### blocks.router Module 173 | The `blocks.router` module contains a routing helper module that assists in adding routes to the $routeProvider. 174 | 175 | ## Gulp Tasks 176 | 177 | ### Task Listing 178 | 179 | - `gulp help` 180 | 181 | Displays all of the available gulp tasks. 182 | 183 | ### Code Analysis 184 | 185 | - `gulp vet` 186 | 187 | Performs static code analysis on all javascript files. Runs jshint and jscs. 188 | 189 | - `gulp vet --verbose` 190 | 191 | Displays all files affected and extended information about the code analysis. 192 | 193 | - `gulp plato` 194 | 195 | Performs code analysis using plato on all javascript files. Plato generates a report in the reports folder. 196 | 197 | ### Testing 198 | 199 | - `gulp serve-specs` 200 | 201 | Serves and browses to the spec runner html page and runs the unit tests in it. Injects any changes on the fly and re runs the tests. Quick and easy view of tests as an alternative to terminal via `gulp test`. 202 | 203 | - `gulp test` 204 | 205 | Runs all unit tests using karma runner, mocha, chai and sinon with phantomjs. Depends on vet task, for code analysis. 206 | 207 | - `gulp test --startServers` 208 | 209 | Runs all unit tests and midway tests. Cranks up a second node process to run a server for the midway tests to hit a web api. 210 | 211 | - `gulp autotest` 212 | 213 | Runs a watch to run all unit tests. 214 | 215 | - `gulp autotest --startServers` 216 | 217 | Runs a watch to run all unit tests and midway tests. Cranks up a second node process to run a server for the midway tests to hit a web api. 218 | 219 | ### Cleaning Up 220 | 221 | - `gulp clean` 222 | 223 | Remove all files from the build and temp folders 224 | 225 | - `gulp clean-images` 226 | 227 | Remove all images from the build folder 228 | 229 | - `gulp clean-code` 230 | 231 | Remove all javascript and html from the build folder 232 | 233 | - `gulp clean-fonts` 234 | 235 | Remove all fonts from the build folder 236 | 237 | - `gulp clean-styles` 238 | 239 | Remove all styles from the build folder 240 | 241 | ### Fonts and Images 242 | 243 | - `gulp fonts` 244 | 245 | Copy all fonts from source to the build folder 246 | 247 | - `gulp images` 248 | 249 | Copy all images from source to the build folder 250 | 251 | ### Styles 252 | 253 | - `gulp styles` 254 | 255 | Compile less files to CSS, add vendor prefixes, and copy to the build folder 256 | 257 | ### Bower Files 258 | 259 | - `gulp wiredep` 260 | 261 | Looks up all bower components' main files and JavaScript source code, then adds them to the `index.html`. 262 | 263 | The `.bowerrc` file also runs this as a postinstall task whenever `bower install` is run. 264 | 265 | ### Angular HTML Templates 266 | 267 | - `gulp templatecache` 268 | 269 | Create an Angular module that adds all HTML templates to Angular's $templateCache. This pre-fetches all HTML templates saving XHR calls for the HTML. 270 | 271 | - `gulp templatecache --verbose` 272 | 273 | Displays all files affected by the task. 274 | 275 | ### Serving Development Code 276 | 277 | - `gulp serve-dev` 278 | 279 | Serves the development code and launches it in a browser. The goal of building for development is to do it as fast as possible, to keep development moving efficiently. This task serves all code from the source folders and compiles less to css in a temp folder. 280 | 281 | - `gulp serve-dev --nosync` 282 | 283 | Serves the development code without launching the browser. 284 | 285 | - `gulp serve-dev --debug` 286 | 287 | Launch debugger with node-inspector. 288 | 289 | - `gulp serve-dev --debug-brk` 290 | 291 | Launch debugger and break on 1st line with node-inspector. 292 | 293 | ### Building Production Code 294 | 295 | - `gulp optimize` 296 | 297 | Optimize all javascript and styles, move to a build folder, and inject them into the new index.html 298 | 299 | - `gulp build` 300 | 301 | Copies all fonts, copies images and runs `gulp optimize` to build the production code to the build folder. 302 | 303 | ### Serving Production Code 304 | 305 | - `gulp serve-build` 306 | 307 | Serve the optimized code from the build folder and launch it in a browser. 308 | 309 | - `gulp serve-build --nosync` 310 | 311 | Serve the optimized code from the build folder and manually launch the browser. 312 | 313 | - `gulp serve-build --debug` 314 | 315 | Launch debugger with node-inspector. 316 | 317 | - `gulp serve-build --debug-brk` 318 | 319 | Launch debugger and break on 1st line with node-inspector. 320 | 321 | ### Bumping Versions 322 | 323 | - `gulp bump` 324 | 325 | Bump the minor version using semver. 326 | --type=patch // default 327 | --type=minor 328 | --type=major 329 | --type=pre 330 | --ver=1.2.3 // specific version 331 | 332 | ## License 333 | 334 | MIT 335 | 336 | ## Credits 337 | This a fork of [Clarkio](https://github.com/clarkio)'s [vulnerable-app](https://github.com/clarkio/vulnerable-app) repo. 338 | -------------------------------------------------------------------------------- /attacker-app/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "scripts": { 4 | "postinstall": "gulp wiredep" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /attacker-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /attacker-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 3 | node_modules/ 4 | bower_components/ 5 | 6 | # other 7 | .tmp 8 | -------------------------------------------------------------------------------- /attacker-app/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": ["node_modules/**", "bower_components/**"], 3 | 4 | "requireCurlyBraces": [ 5 | "if", 6 | "else", 7 | "for", 8 | "while", 9 | "do", 10 | "try", 11 | "catch" 12 | ], 13 | "requireOperatorBeforeLineBreak": true, 14 | "requireCamelCaseOrUpperCaseIdentifiers": true, 15 | "maximumLineLength": { 16 | "value": 100, 17 | "allowComments": true, 18 | "allowRegex": true 19 | }, 20 | "validateIndentation": 4, 21 | "validateQuoteMarks": "'", 22 | 23 | "disallowMultipleLineStrings": true, 24 | "disallowMixedSpacesAndTabs": true, 25 | "disallowTrailingWhitespace": true, 26 | "disallowSpaceAfterPrefixUnaryOperators": true, 27 | "disallowMultipleVarDecl": null, 28 | 29 | "requireSpaceAfterKeywords": [ 30 | "if", 31 | "else", 32 | "for", 33 | "while", 34 | "do", 35 | "switch", 36 | "return", 37 | "try", 38 | "catch" 39 | ], 40 | "requireSpaceBeforeBinaryOperators": [ 41 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 42 | "&=", "|=", "^=", "+=", 43 | 44 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 45 | "|", "^", "&&", "||", "===", "==", ">=", 46 | "<=", "<", ">", "!=", "!==" 47 | ], 48 | "requireSpaceAfterBinaryOperators": true, 49 | "requireSpacesInConditionalExpression": true, 50 | "requireSpaceBeforeBlockStatements": true, 51 | "requireLineFeedAtFileEnd": true, 52 | "disallowSpacesInsideObjectBrackets": "all", 53 | "disallowSpacesInsideArrayBrackets": "all", 54 | "disallowSpacesInsideParentheses": true, 55 | 56 | "jsDoc": { 57 | "checkAnnotations": true, 58 | "checkParamNames": true, 59 | "requireParamTypes": true, 60 | "checkReturnTypes": true, 61 | "checkTypes": true 62 | }, 63 | 64 | "disallowMultipleLineBreaks": true, 65 | 66 | "disallowCommaBeforeLineBreak": null, 67 | "disallowDanglingUnderscores": null, 68 | "disallowEmptyBlocks": null, 69 | "disallowTrailingComma": null, 70 | "requireCommaBeforeLineBreak": null, 71 | "requireDotNotation": null, 72 | "requireMultipleVarDecl": null, 73 | "requireParenthesesAroundIIFE": true 74 | } 75 | -------------------------------------------------------------------------------- /attacker-app/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": "nofunc", 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "single", 19 | "undef": true, 20 | "unused": false, 21 | "strict": false, 22 | "maxparams": 10, 23 | "maxdepth": 5, 24 | "maxstatements": 40, 25 | "maxcomplexity": 8, 26 | "maxlen": 120, 27 | 28 | "asi": false, 29 | "boss": false, 30 | "debug": false, 31 | "eqnull": true, 32 | "esnext": false, 33 | "evil": false, 34 | "expr": false, 35 | "funcscope": false, 36 | "globalstrict": false, 37 | "iterator": false, 38 | "lastsemic": false, 39 | "laxbreak": false, 40 | "laxcomma": false, 41 | "loopfunc": true, 42 | "maxerr": 50, 43 | "moz": false, 44 | "multistr": false, 45 | "notypeof": false, 46 | "proto": false, 47 | "scripturl": false, 48 | "shadow": false, 49 | "sub": true, 50 | "supernew": false, 51 | "validthis": false, 52 | "noyield": false, 53 | 54 | "browser": true, 55 | "node": true, 56 | 57 | "globals": { 58 | "angular": false 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /attacker-app/README.md: -------------------------------------------------------------------------------- 1 | # attacker-app 2 | 3 | **Generated from HotTowel Angular** 4 | 5 | >*Opinionated Angular style guide for teams by [@john_papa](//twitter.com/john_papa)* 6 | 7 | >More details about the styles and patterns used in this app can be found in my [Angular Style Guide](https://github.com/johnpapa/angularjs-styleguide) and my [Angular Patterns: Clean Code](http://jpapa.me/ngclean) course at [Pluralsight](http://pluralsight.com/training/Authors/Details/john-papa) and working in teams. 8 | 9 | ## Prerequisites 10 | 11 | 1. Install [Node.js](http://nodejs.org) 12 | - on OSX use [homebrew](http://brew.sh) `brew install node` 13 | - on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs` 14 | 15 | 2. Install Yeoman `npm install -g yo` 16 | 17 | 3. Install these NPM packages globally 18 | 19 | ```bash 20 | npm install -g bower gulp nodemon 21 | ``` 22 | 23 | >Refer to these [instructions on how to not require sudo](https://github.com/sindresorhus/guides/blob/master/npm-global-without-sudo.md) 24 | 25 | ## Running HotTowel 26 | 27 | ### Linting 28 | - Run code analysis using `gulp vet`. This runs jshint, jscs, and plato. 29 | 30 | ### Tests 31 | - Run the unit tests using `gulp test` (via karma, mocha, sinon). 32 | 33 | ### Running in dev mode 34 | - Run the project with `gulp serve-dev` 35 | 36 | - opens it in a browser and updates the browser with any files changes. 37 | 38 | ### Building the project 39 | - Build the optimized project using `gulp build` 40 | - This create the optimized code for the project and puts it in the build folder 41 | 42 | ### Running the optimized code 43 | - Run the optimize project from the build folder with `gulp serve-build` 44 | 45 | ## Exploring HotTowel 46 | HotTowel Angular starter project 47 | 48 | ### Structure 49 | The structure also contains a gulpfile.js and a server folder. The server is there just so we can serve the app using node. Feel free to use any server you wish. 50 | 51 | /src 52 | /client 53 | /app 54 | /content 55 | 56 | ### Installing Packages 57 | When you generate the project it should run these commands, but if you notice missing packages, run these again: 58 | 59 | - `npm install` 60 | - `bower install` 61 | 62 | ### The Modules 63 | The app has 4 feature modules and depends on a series of external modules and custom but cross-app modules 64 | 65 | ``` 66 | app --> [ 67 | app.admin --> [ 68 | app.core, 69 | app.widgets 70 | ], 71 | app.dashboard --> [ 72 | app.core, 73 | app.widgets 74 | ], 75 | app.layout --> [ 76 | app.core 77 | ], 78 | app.widgets, 79 | app.core --> [ 80 | ngAnimate, 81 | ngSanitize, 82 | ui.router, 83 | blocks.exception, 84 | blocks.logger, 85 | blocks.router 86 | ] 87 | ] 88 | ``` 89 | 90 | #### core Module 91 | Core modules are ones that are shared throughout the entire application and may be customized for the specific application. Example might be common data services. 92 | 93 | This is an aggregator of modules that the application will need. The `core` module takes the blocks, common, and Angular sub-modules as dependencies. 94 | 95 | #### blocks Modules 96 | Block modules are reusable blocks of code that can be used across projects simply by including them as dependencies. 97 | 98 | ##### blocks.logger Module 99 | The `blocks.logger` module handles logging across the Angular app. 100 | 101 | ##### blocks.exception Module 102 | The `blocks.exception` module handles exceptions across the Angular app. 103 | 104 | It depends on the `blocks.logger` module, because the implementation logs the exceptions. 105 | 106 | ##### blocks.router Module 107 | The `blocks.router` module contains a routing helper module that assists in adding routes to the $routeProvider. 108 | 109 | ## Gulp Tasks 110 | 111 | ### Task Listing 112 | 113 | - `gulp help` 114 | 115 | Displays all of the available gulp tasks. 116 | 117 | ### Code Analysis 118 | 119 | - `gulp vet` 120 | 121 | Performs static code analysis on all javascript files. Runs jshint and jscs. 122 | 123 | - `gulp vet --verbose` 124 | 125 | Displays all files affected and extended information about the code analysis. 126 | 127 | - `gulp plato` 128 | 129 | Performs code analysis using plato on all javascript files. Plato generates a report in the reports folder. 130 | 131 | ### Testing 132 | 133 | - `gulp serve-specs` 134 | 135 | Serves and browses to the spec runner html page and runs the unit tests in it. Injects any changes on the fly and re runs the tests. Quick and easy view of tests as an alternative to terminal via `gulp test`. 136 | 137 | - `gulp test` 138 | 139 | Runs all unit tests using karma runner, mocha, chai and sinon with phantomjs. Depends on vet task, for code analysis. 140 | 141 | - `gulp test --startServers` 142 | 143 | Runs all unit tests and midway tests. Cranks up a second node process to run a server for the midway tests to hit a web api. 144 | 145 | - `gulp autotest` 146 | 147 | Runs a watch to run all unit tests. 148 | 149 | - `gulp autotest --startServers` 150 | 151 | Runs a watch to run all unit tests and midway tests. Cranks up a second node process to run a server for the midway tests to hit a web api. 152 | 153 | ### Cleaning Up 154 | 155 | - `gulp clean` 156 | 157 | Remove all files from the build and temp folders 158 | 159 | - `gulp clean-images` 160 | 161 | Remove all images from the build folder 162 | 163 | - `gulp clean-code` 164 | 165 | Remove all javascript and html from the build folder 166 | 167 | - `gulp clean-fonts` 168 | 169 | Remove all fonts from the build folder 170 | 171 | - `gulp clean-styles` 172 | 173 | Remove all styles from the build folder 174 | 175 | ### Fonts and Images 176 | 177 | - `gulp fonts` 178 | 179 | Copy all fonts from source to the build folder 180 | 181 | - `gulp images` 182 | 183 | Copy all images from source to the build folder 184 | 185 | ### Styles 186 | 187 | - `gulp styles` 188 | 189 | Compile less files to CSS, add vendor prefixes, and copy to the build folder 190 | 191 | ### Bower Files 192 | 193 | - `gulp wiredep` 194 | 195 | Looks up all bower components' main files and JavaScript source code, then adds them to the `index.html`. 196 | 197 | The `.bowerrc` file also runs this as a postinstall task whenever `bower install` is run. 198 | 199 | ### Angular HTML Templates 200 | 201 | - `gulp templatecache` 202 | 203 | Create an Angular module that adds all HTML templates to Angular's $templateCache. This pre-fetches all HTML templates saving XHR calls for the HTML. 204 | 205 | - `gulp templatecache --verbose` 206 | 207 | Displays all files affected by the task. 208 | 209 | ### Serving Development Code 210 | 211 | - `gulp serve-dev` 212 | 213 | Serves the development code and launches it in a browser. The goal of building for development is to do it as fast as possible, to keep development moving efficiently. This task serves all code from the source folders and compiles less to css in a temp folder. 214 | 215 | - `gulp serve-dev --nosync` 216 | 217 | Serves the development code without launching the browser. 218 | 219 | - `gulp serve-dev --debug` 220 | 221 | Launch debugger with node-inspector. 222 | 223 | - `gulp serve-dev --debug-brk` 224 | 225 | Launch debugger and break on 1st line with node-inspector. 226 | 227 | ### Building Production Code 228 | 229 | - `gulp optimize` 230 | 231 | Optimize all javascript and styles, move to a build folder, and inject them into the new index.html 232 | 233 | - `gulp build` 234 | 235 | Copies all fonts, copies images and runs `gulp optimize` to build the production code to the build folder. 236 | 237 | ### Serving Production Code 238 | 239 | - `gulp serve-build` 240 | 241 | Serve the optimized code from the build folder and launch it in a browser. 242 | 243 | - `gulp serve-build --nosync` 244 | 245 | Serve the optimized code from the build folder and manually launch the browser. 246 | 247 | - `gulp serve-build --debug` 248 | 249 | Launch debugger with node-inspector. 250 | 251 | - `gulp serve-build --debug-brk` 252 | 253 | Launch debugger and break on 1st line with node-inspector. 254 | 255 | ### Bumping Versions 256 | 257 | - `gulp bump` 258 | 259 | Bump the minor version using semver. 260 | --type=patch // default 261 | --type=minor 262 | --type=major 263 | --type=pre 264 | --ver=1.2.3 // specific version 265 | 266 | ## License 267 | 268 | MIT 269 | -------------------------------------------------------------------------------- /attacker-app/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "attacker-app", 3 | "version": "0.0.1", 4 | "description": "attacker-app", 5 | "authors": [], 6 | "license": "MIT", 7 | "ignore": [ 8 | "**/.*", 9 | "node_modules", 10 | "bower_components", 11 | "test", 12 | "tests" 13 | ], 14 | "devDependencies": { 15 | "angular-mocks": "^1.4.5", 16 | "sinon": "http://sinonjs.org/releases/sinon-1.12.1.js", 17 | "bardjs": "^0.1.4" 18 | }, 19 | "dependencies": { 20 | "jquery": "^2.1.4", 21 | "angular": "^1.4.5", 22 | "angular-sanitize": "^1.4.5", 23 | "bootstrap": "^3.3.5", 24 | "extras.angular.plus": "^0.9.2", 25 | "font-awesome": "^4.3.0", 26 | "moment": "^2.10.3", 27 | "angular-ui-router": "^0.2.15", 28 | "toastr": "^2.1.1", 29 | "angular-animate": "^1.4.5" 30 | }, 31 | "resolutions": { 32 | "angular": "1.3.17" 33 | }, 34 | "overrides": { 35 | "bootstrap": { 36 | "main": "dist/css/bootstrap.css", 37 | "dist": "dist/js/bootstrap.js" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /attacker-app/gulp.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | var client = './src/client/'; 3 | var server = './src/server/'; 4 | var clientApp = client + 'app/'; 5 | var report = './report/'; 6 | var root = './'; 7 | var specRunnerFile = 'specs.html'; 8 | var temp = './.tmp/'; 9 | var wiredep = require('wiredep'); 10 | var bowerFiles = wiredep({devDependencies: true})['js']; 11 | var bower = { 12 | json: require('./bower.json'), 13 | directory: './bower_components/', 14 | ignorePath: '../..' 15 | }; 16 | var nodeModules = 'node_modules'; 17 | 18 | var config = { 19 | /** 20 | * File paths 21 | */ 22 | // all javascript that we want to vet 23 | alljs: [ 24 | './src/**/*.js', 25 | './*.js' 26 | ], 27 | build: './build/', 28 | client: client, 29 | css: temp + 'styles.css', 30 | fonts: bower.directory + 'font-awesome/fonts/**/*.*', 31 | html: client + '**/*.html', 32 | htmltemplates: clientApp + '**/*.html', 33 | images: client + 'images/**/*.*', 34 | index: client + 'index.html', 35 | // app js, with no specs 36 | js: [ 37 | clientApp + '**/*.module.js', 38 | clientApp + '**/*.js', 39 | '!' + clientApp + '**/*.spec.js' 40 | ], 41 | jsOrder: [ 42 | '**/app.module.js', 43 | '**/*.module.js', 44 | '**/*.js' 45 | ], 46 | less: client + 'styles/styles.less', 47 | report: report, 48 | root: root, 49 | server: server, 50 | source: 'src/', 51 | stubsjs: [ 52 | bower.directory + 'angular-mocks/angular-mocks.js', 53 | client + 'stubs/**/*.js' 54 | ], 55 | temp: temp, 56 | 57 | /** 58 | * optimized files 59 | */ 60 | optimized: { 61 | app: 'app.js', 62 | lib: 'lib.js' 63 | }, 64 | 65 | /** 66 | * plato 67 | */ 68 | plato: {js: clientApp + '**/*.js'}, 69 | 70 | /** 71 | * browser sync 72 | */ 73 | browserReloadDelay: 1000, 74 | 75 | /** 76 | * template cache 77 | */ 78 | templateCache: { 79 | file: 'templates.js', 80 | options: { 81 | module: 'app.core', 82 | root: 'app/', 83 | standalone: false 84 | } 85 | }, 86 | 87 | /** 88 | * Bower and NPM files 89 | */ 90 | bower: bower, 91 | packages: [ 92 | './package.json', 93 | './bower.json' 94 | ], 95 | 96 | /** 97 | * specs.html, our HTML spec runner 98 | */ 99 | specRunner: client + specRunnerFile, 100 | specRunnerFile: specRunnerFile, 101 | 102 | /** 103 | * The sequence of the injections into specs.html: 104 | * 1 testlibraries 105 | * mocha setup 106 | * 2 bower 107 | * 3 js 108 | * 4 spechelpers 109 | * 5 specs 110 | * 6 templates 111 | */ 112 | testlibraries: [ 113 | nodeModules + '/mocha/mocha.js', 114 | nodeModules + '/chai/chai.js', 115 | nodeModules + '/sinon-chai/lib/sinon-chai.js' 116 | ], 117 | specHelpers: [client + 'test-helpers/*.js'], 118 | specs: [clientApp + '**/*.spec.js'], 119 | serverIntegrationSpecs: [client + '/tests/server-integration/**/*.spec.js'], 120 | 121 | /** 122 | * Node settings 123 | */ 124 | nodeServer: server + 'app.js', 125 | defaultPort: '8002' 126 | }; 127 | 128 | /** 129 | * wiredep and bower settings 130 | */ 131 | config.getWiredepDefaultOptions = function() { 132 | var options = { 133 | bowerJson: config.bower.json, 134 | directory: config.bower.directory, 135 | ignorePath: config.bower.ignorePath 136 | }; 137 | return options; 138 | }; 139 | 140 | /** 141 | * karma settings 142 | */ 143 | config.karma = getKarmaOptions(); 144 | 145 | return config; 146 | 147 | //////////////// 148 | 149 | function getKarmaOptions() { 150 | var options = { 151 | files: [].concat( 152 | bowerFiles, 153 | config.specHelpers, 154 | clientApp + '**/*.module.js', 155 | clientApp + '**/*.js', 156 | temp + config.templateCache.file, 157 | config.serverIntegrationSpecs 158 | ), 159 | exclude: [], 160 | coverage: { 161 | dir: report + 'coverage', 162 | reporters: [ 163 | // reporters not supporting the `file` property 164 | {type: 'html', subdir: 'report-html'}, 165 | {type: 'lcov', subdir: 'report-lcov'}, 166 | {type: 'text-summary'} //, subdir: '.', file: 'text-summary.txt'} 167 | ] 168 | }, 169 | preprocessors: {} 170 | }; 171 | options.preprocessors[clientApp + '**/!(*.spec)+(.js)'] = ['coverage']; 172 | return options; 173 | } 174 | }; 175 | -------------------------------------------------------------------------------- /attacker-app/gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/attacker-app/gulp.png -------------------------------------------------------------------------------- /attacker-app/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | var gulpConfig = require('./gulp.config')(); 3 | 4 | config.set({ 5 | // base path that will be used to resolve all patterns (eg. files, exclude) 6 | basePath: './', 7 | 8 | // frameworks to use 9 | // some available frameworks: https://npmjs.org/browse/keyword/karma-adapter 10 | frameworks: ['mocha', 'chai', 'sinon', 'chai-sinon'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: gulpConfig.karma.files, 14 | 15 | // list of files to exclude 16 | exclude: gulpConfig.karma.exclude, 17 | 18 | proxies: { 19 | '/': 'http://localhost:8888/' 20 | }, 21 | 22 | // preprocess matching files before serving them to the browser 23 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 24 | preprocessors: gulpConfig.karma.preprocessors, 25 | 26 | // test results reporter to use 27 | // possible values: 'dots', 'progress', 'coverage' 28 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 29 | reporters: ['progress', 'coverage'], 30 | 31 | coverageReporter: { 32 | dir: gulpConfig.karma.coverage.dir, 33 | reporters: gulpConfig.karma.coverage.reporters 34 | }, 35 | 36 | // web server port 37 | port: 9876, 38 | 39 | // enable / disable colors in the output (reporters and logs) 40 | colors: true, 41 | 42 | // level of logging 43 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || 44 | // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 45 | logLevel: config.LOG_INFO, 46 | 47 | // enable / disable watching file and executing tests whenever any file changes 48 | autoWatch: true, 49 | 50 | // start these browsers 51 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 52 | // browsers: ['Chrome', 'ChromeCanary', 'FirefoxAurora', 'Safari', 'PhantomJS'], 53 | browsers: ['PhantomJS'], 54 | 55 | // Continuous Integration mode 56 | // if true, Karma captures browsers, runs the tests and exits 57 | singleRun: false 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /attacker-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "attacker-app", 3 | "description": "attacker-app Project Generated from HotTowel Angular", 4 | "version": "0.0.0", 5 | "scripts": { 6 | "init": "npm install", 7 | "install": "bower install", 8 | "start": "node src/server/app.js", 9 | "test": "gulp test" 10 | }, 11 | "dependencies": { 12 | "body-parser": "^1.8.2", 13 | "express": "^4.9.3", 14 | "morgan": "^1.1.1", 15 | "serve-favicon": "^2.0.1" 16 | }, 17 | "devDependencies": { 18 | "browser-sync": "^2.7.13", 19 | "chai": "^3.1.0", 20 | "chai-as-promised": "^5.1.0", 21 | "chalk": "^1.1.0", 22 | "dateformat": "^1.0.8-1.2.3", 23 | "debug": "^2.0.0", 24 | "del": "^1.2.0", 25 | "glob": "^4.5.3", 26 | "gulp": "^3.8.10", 27 | "gulp-angular-templatecache": "^1.4.2", 28 | "gulp-autoprefixer": "^2.3.1", 29 | "gulp-bump": "^0.3.1", 30 | "gulp-bytediff": "^0.2.0", 31 | "gulp-concat": "^2.3.3", 32 | "gulp-filter": "^2.0.2", 33 | "gulp-header": "^1.2.2", 34 | "gulp-if": "^1.2.5", 35 | "gulp-imagemin": "^2.3.0", 36 | "gulp-inject": "^1.0.1", 37 | "gulp-jscs": "^2.0.0", 38 | "gulp-jshint": "^1.7.1", 39 | "gulp-less": "^3.0.1", 40 | "gulp-load-plugins": "^1.0.0-rc.1", 41 | "gulp-minify-css": "^1.1.1", 42 | "gulp-minify-html": "^1.0.4", 43 | "gulp-ng-annotate": "^1.0.0", 44 | "gulp-nodemon": "^2.0.3", 45 | "gulp-order": "^1.1.1", 46 | "gulp-plumber": "^1.0.1", 47 | "gulp-print": "^1.1.0", 48 | "gulp-rev": "^5.1.0", 49 | "gulp-rev-replace": "^0.4.2", 50 | "gulp-sourcemaps": "^1.1.5", 51 | "gulp-task-listing": "^1.0.0", 52 | "gulp-uglify": "^1.0.1", 53 | "gulp-useref": "^1.0.2", 54 | "gulp-util": "^3.0.1", 55 | "jshint-stylish": "^2.0.1", 56 | "karma": "^0.13.2", 57 | "karma-chai": "^0.1.0", 58 | "karma-chai-sinon": "^0.1.3", 59 | "karma-chrome-launcher": "^0.2.0", 60 | "karma-coverage": "^0.4.2", 61 | "karma-firefox-launcher": "^0.1.3", 62 | "karma-growl-reporter": "^0.1.1", 63 | "karma-mocha": "^0.2.0", 64 | "karma-phantomjs-launcher": "^0.2.0", 65 | "karma-safari-launcher": "^0.1.1", 66 | "karma-sinon": "^1.0.3", 67 | "lodash": "^3.10.0", 68 | "method-override": "^2.3.4", 69 | "minimist": "^1.1.0", 70 | "mocha": "^2.2.5", 71 | "node-notifier": "^4.0.3", 72 | "phantomjs": "^1.9.17", 73 | "plato": "^1.2.0", 74 | "q": "^1.0.1", 75 | "sinon": "^1.12.2", 76 | "sinon-chai": "^2.6.0", 77 | "wiredep": "^2.2.2", 78 | "yargs": "^3.15.0" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/app.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app', [ 5 | 'app.core', 6 | 'app.widgets', 7 | 'app.dashboard', 8 | 'app.csrf-attack', 9 | 'app.clickjacking-attack', 10 | 'app.layout' 11 | ]); 12 | 13 | })(); 14 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/exception/exception-handler.provider.js: -------------------------------------------------------------------------------- 1 | // Include in index.html so that app level exceptions are handled. 2 | // Exclude from testRunner.html which should run exactly what it wants to run 3 | (function() { 4 | 'use strict'; 5 | 6 | angular 7 | .module('blocks.exception') 8 | .provider('exceptionHandler', exceptionHandlerProvider) 9 | .config(config); 10 | 11 | /** 12 | * Must configure the exception handling 13 | */ 14 | function exceptionHandlerProvider() { 15 | /* jshint validthis:true */ 16 | this.config = { 17 | appErrorPrefix: undefined 18 | }; 19 | 20 | this.configure = function (appErrorPrefix) { 21 | this.config.appErrorPrefix = appErrorPrefix; 22 | }; 23 | 24 | this.$get = function() { 25 | return {config: this.config}; 26 | }; 27 | } 28 | 29 | config.$inject = ['$provide']; 30 | 31 | /** 32 | * Configure by setting an optional string value for appErrorPrefix. 33 | * Accessible via config.appErrorPrefix (via config value). 34 | * @param {Object} $provide 35 | */ 36 | /* @ngInject */ 37 | function config($provide) { 38 | $provide.decorator('$exceptionHandler', extendExceptionHandler); 39 | } 40 | 41 | extendExceptionHandler.$inject = ['$delegate', 'exceptionHandler', 'logger']; 42 | 43 | /** 44 | * Extend the $exceptionHandler service to also display a toast. 45 | * @param {Object} $delegate 46 | * @param {Object} exceptionHandler 47 | * @param {Object} logger 48 | * @return {Function} the decorated $exceptionHandler service 49 | */ 50 | function extendExceptionHandler($delegate, exceptionHandler, logger) { 51 | return function(exception, cause) { 52 | var appErrorPrefix = exceptionHandler.config.appErrorPrefix || ''; 53 | var errorData = {exception: exception, cause: cause}; 54 | exception.message = appErrorPrefix + exception.message; 55 | $delegate(exception, cause); 56 | /** 57 | * Could add the error to a service's collection, 58 | * add errors to $rootScope, log errors to remote web server, 59 | * or log locally. Or throw hard. It is entirely up to you. 60 | * throw exception; 61 | * 62 | * @example 63 | * throw { message: 'error message we added' }; 64 | */ 65 | logger.error(exception.message, errorData); 66 | }; 67 | } 68 | })(); 69 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/exception/exception-handler.provider.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('blocks.exception', function() { 3 | var exceptionHandlerProvider; 4 | var mocks = { 5 | errorMessage: 'fake error', 6 | prefix: '[TEST]: ' 7 | }; 8 | 9 | beforeEach(function() { 10 | bard.appModule('blocks.exception', function(_exceptionHandlerProvider_) { 11 | exceptionHandlerProvider = _exceptionHandlerProvider_; 12 | }); 13 | bard.inject('$rootScope'); 14 | }); 15 | 16 | bard.verifyNoOutstandingHttpRequests(); 17 | 18 | describe('exceptionHandlerProvider', function() { 19 | it('should have a dummy test', inject(function() { 20 | expect(true).to.equal(true); 21 | })); 22 | 23 | it('should have exceptionHandlerProvider defined', inject(function() { 24 | expect(exceptionHandlerProvider).to.be.defined; 25 | })); 26 | 27 | it('should have configuration', inject(function() { 28 | expect(exceptionHandlerProvider.config).to.be.defined; 29 | })); 30 | 31 | it('should have configuration', inject(function() { 32 | expect(exceptionHandlerProvider.configure).to.be.defined; 33 | })); 34 | 35 | describe('with appErrorPrefix', function() { 36 | beforeEach(function() { 37 | exceptionHandlerProvider.configure(mocks.prefix); 38 | }); 39 | 40 | it('should have appErrorPrefix defined', inject(function() { 41 | expect(exceptionHandlerProvider.$get().config.appErrorPrefix).to.be.defined; 42 | })); 43 | 44 | it('should have appErrorPrefix set properly', inject(function() { 45 | expect(exceptionHandlerProvider.$get().config.appErrorPrefix) 46 | .to.equal(mocks.prefix); 47 | })); 48 | 49 | it('should throw an error when forced', inject(function() { 50 | expect(functionThatWillThrow).to.throw(); 51 | })); 52 | 53 | it('manual error is handled by decorator', function() { 54 | var exception; 55 | exceptionHandlerProvider.configure(mocks.prefix); 56 | try { 57 | $rootScope.$apply(functionThatWillThrow); 58 | } 59 | catch (ex) { 60 | exception = ex; 61 | expect(ex.message).to.equal(mocks.prefix + mocks.errorMessage); 62 | } 63 | }); 64 | }); 65 | }); 66 | 67 | function functionThatWillThrow() { 68 | throw new Error(mocks.errorMessage); 69 | } 70 | }); 71 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/exception/exception.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('blocks.exception') 6 | .factory('exception', exception); 7 | 8 | /* @ngInject */ 9 | function exception($q, logger) { 10 | var service = { 11 | catcher: catcher 12 | }; 13 | return service; 14 | 15 | function catcher(message) { 16 | return function(e) { 17 | var thrownDescription; 18 | var newMessage; 19 | if (e.data && e.data.description) { 20 | thrownDescription = '\n' + e.data.description; 21 | newMessage = message + thrownDescription; 22 | } 23 | e.data.description = newMessage; 24 | logger.error(newMessage); 25 | return $q.reject(e); 26 | }; 27 | } 28 | } 29 | })(); 30 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/exception/exception.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('blocks.exception', ['blocks.logger']); 5 | })(); 6 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/logger/logger.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('blocks.logger') 6 | .factory('logger', logger); 7 | 8 | logger.$inject = ['$log', 'toastr']; 9 | 10 | /* @ngInject */ 11 | function logger($log, toastr) { 12 | var service = { 13 | showToasts: true, 14 | 15 | error : error, 16 | info : info, 17 | success : success, 18 | warning : warning, 19 | 20 | // straight to console; bypass toastr 21 | log : $log.log 22 | }; 23 | 24 | return service; 25 | ///////////////////// 26 | 27 | function error(message, data, title) { 28 | toastr.error(message, title); 29 | $log.error('Error: ' + message, data); 30 | } 31 | 32 | function info(message, data, title) { 33 | toastr.info(message, title); 34 | $log.info('Info: ' + message, data); 35 | } 36 | 37 | function success(message, data, title) { 38 | toastr.success(message, title); 39 | $log.info('Success: ' + message, data); 40 | } 41 | 42 | function warning(message, data, title) { 43 | toastr.warning(message, title); 44 | $log.warn('Warning: ' + message, data); 45 | } 46 | } 47 | }()); 48 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/logger/logger.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('blocks.logger', []); 5 | })(); 6 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/router/router-helper.provider.js: -------------------------------------------------------------------------------- 1 | /* Help configure the state-base ui.router */ 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('blocks.router') 7 | .provider('routerHelper', routerHelperProvider); 8 | 9 | routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; 10 | /* @ngInject */ 11 | function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { 12 | /* jshint validthis:true */ 13 | var config = { 14 | docTitle: undefined, 15 | resolveAlways: {} 16 | }; 17 | 18 | $locationProvider.html5Mode(true); 19 | 20 | this.configure = function(cfg) { 21 | angular.extend(config, cfg); 22 | }; 23 | 24 | this.$get = RouterHelper; 25 | RouterHelper.$inject = ['$location', '$rootScope', '$state', 'logger']; 26 | /* @ngInject */ 27 | function RouterHelper($location, $rootScope, $state, logger) { 28 | var handlingStateChangeError = false; 29 | var hasOtherwise = false; 30 | var stateCounts = { 31 | errors: 0, 32 | changes: 0 33 | }; 34 | 35 | var service = { 36 | configureStates: configureStates, 37 | getStates: getStates, 38 | stateCounts: stateCounts 39 | }; 40 | 41 | init(); 42 | 43 | return service; 44 | 45 | /////////////// 46 | 47 | function configureStates(states, otherwisePath) { 48 | states.forEach(function(state) { 49 | state.config.resolve = 50 | angular.extend(state.config.resolve || {}, config.resolveAlways); 51 | $stateProvider.state(state.state, state.config); 52 | }); 53 | if (otherwisePath && !hasOtherwise) { 54 | hasOtherwise = true; 55 | $urlRouterProvider.otherwise(otherwisePath); 56 | } 57 | } 58 | 59 | function handleRoutingErrors() { 60 | // Route cancellation: 61 | // On routing error, go to the dashboard. 62 | // Provide an exit clause if it tries to do it twice. 63 | $rootScope.$on('$stateChangeError', 64 | function(event, toState, toParams, fromState, fromParams, error) { 65 | if (handlingStateChangeError) { 66 | return; 67 | } 68 | stateCounts.errors++; 69 | handlingStateChangeError = true; 70 | var destination = (toState && 71 | (toState.title || toState.name || toState.loadedTemplateUrl)) || 72 | 'unknown target'; 73 | var msg = 'Error routing to ' + destination + '. ' + 74 | (error.data || '') + '.
' + (error.statusText || '') + 75 | ': ' + (error.status || ''); 76 | logger.warning(msg, [toState]); 77 | $location.path('/'); 78 | } 79 | ); 80 | } 81 | 82 | function init() { 83 | handleRoutingErrors(); 84 | updateDocTitle(); 85 | } 86 | 87 | function getStates() { return $state.get(); } 88 | 89 | function updateDocTitle() { 90 | $rootScope.$on('$stateChangeSuccess', 91 | function(event, toState, toParams, fromState, fromParams) { 92 | stateCounts.changes++; 93 | handlingStateChangeError = false; 94 | var title = config.docTitle + ' ' + (toState.title || ''); 95 | $rootScope.title = title; // data bind to 96 | } 97 | ); 98 | } 99 | } 100 | } 101 | })(); 102 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/blocks/router/router.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('blocks.router', [ 5 | 'ui.router', 6 | 'blocks.logger' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/clickjacking-attack/clickjacking-attack.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.clickjacking-attack') 6 | .controller('ClickjackingController', ClickjackingController); 7 | 8 | ClickjackingController.$inject = ['$q', 'logger']; 9 | /* @ngInject */ 10 | function ClickjackingController($q, logger) { 11 | var vm = this; 12 | vm.title = 'Clickjacking-Attack'; 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/clickjacking-attack/clickjacking-attack.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('ClickjackingController', function() { 3 | var controller; 4 | var people = mockData.getMockPeople(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.clickjacking-attack'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'dataservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(dataservice, 'getPeople').returns($q.when(people)); 13 | controller = $controller('ClickjackingController'); 14 | $rootScope.$apply(); 15 | }); 16 | 17 | bard.verifyNoOutstandingHttpRequests(); 18 | 19 | describe('Clickjacking controller', function() { 20 | it('should be created successfully', function () { 21 | expect(controller).to.be.defined; 22 | }); 23 | 24 | describe('after activate', function() { 25 | it('should have title of Clickjacking', function () { 26 | expect(controller.title).to.equal('Clickjacking-Attack'); 27 | }); 28 | 29 | it('should have logged "Activated"', function() { 30 | expect($log.info.logs).to.match(/Activated/); 31 | }); 32 | 33 | it('should have news', function () { 34 | expect(controller.news).to.not.be.empty; 35 | }); 36 | 37 | it('should have at least 1 person', function () { 38 | expect(controller.people).to.have.length.above(0); 39 | }); 40 | 41 | it('should have people count of 5', function () { 42 | expect(controller.people).to.have.length(7); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/clickjacking-attack/clickjacking-attack.html: -------------------------------------------------------------------------------- 1 | <section id="clickjacking-view" class="mainbar"> 2 | <style> 3 | iframe { /* iframe from vulnerable-app */ 4 | width:1000px; 5 | height:500px; 6 | position:absolute; 7 | top:0; left:0; 8 | filter:alpha(opacity=10); /* in real life opacity=0 */ 9 | opacity:0.1; 10 | } 11 | </style> 12 | <button style="z-index:-1;margin-top:195px;margin-left:320px;">Click to see awesome dog back flips!</button> 13 | <iframe src="http://localhost:3000/clickjacking" width="800" height="400"></iframe> 14 | </section> -------------------------------------------------------------------------------- /attacker-app/src/client/app/clickjacking-attack/clickjacking-attack.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.clickjacking-attack', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/clickjacking-attack/clickjacking-attack.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.clickjacking-attack') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'clickjacking', 18 | config: { 19 | url: '/', 20 | templateUrl: 'app/clickjacking-attack/clickjacking-attack.html', 21 | controller: 'ClickjackingController', 22 | controllerAs: 'vm', 23 | title: 'Clickjacking-Attack', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-clickjacking"></i> Clickjacking-Attack' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/clickjacking-attack/clickjacking-attack.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('clickjacking routes', function () { 3 | describe('state', function () { 4 | var view = 'app/clickjacking-attack/clickjacking-attack.html'; 5 | 6 | beforeEach(function() { 7 | module('app.clickjacking-attack', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state clickjacking to url / ', function() { 18 | expect($state.href('clickjacking', {})).to.equal('/'); 19 | }); 20 | 21 | it('should map /clickjacking route to clickjacking View template', function () { 22 | expect($state.get('clickjacking').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of clickjacking should work with $state.go', function () { 26 | $state.go('clickjacking'); 27 | $rootScope.$apply(); 28 | expect($state.is('clickjacking')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/404.html: -------------------------------------------------------------------------------- 1 | <section id="dashboard-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-12"> 6 | <ul class="today-datas"> 7 | <li class="bred"> 8 | <div class="pull-left"><i class="fa fa-warning"></i></div> 9 | <div class="datas-text pull-right"> 10 | <a><span class="bold">404</span></a>Page Not Found 11 | </div> 12 | <div class="clearfix"></div> 13 | </li> 14 | </ul> 15 | </div> 16 | </div> 17 | <div class="row"> 18 | <div class="widget wblue"> 19 | <div ht-widget-header title="Page Not Found" 20 | allow-collapse="true"></div> 21 | <div class="widget-content text-center text-info"> 22 | <div class="container"> 23 | No soup for you! 24 | </div> 25 | </div> 26 | <div class="widget-foot"> 27 | <div class="clearfix"></div> 28 | </div> 29 | </div> 30 | </div> 31 | </div> 32 | </section> 33 | </section> 34 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/config.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var core = angular.module('app.core'); 5 | 6 | core.config(toastrConfig); 7 | 8 | toastrConfig.$inject = ['toastr']; 9 | /* @ngInject */ 10 | function toastrConfig(toastr) { 11 | toastr.options.timeOut = 4000; 12 | toastr.options.positionClass = 'toast-bottom-right'; 13 | } 14 | 15 | var config = { 16 | appErrorPrefix: '[attacker-app Error] ', 17 | appTitle: 'attacker-app' 18 | }; 19 | 20 | core.value('config', config); 21 | 22 | core.config(configure); 23 | 24 | configure.$inject = ['$logProvider', 'routerHelperProvider', 'exceptionHandlerProvider']; 25 | /* @ngInject */ 26 | function configure($logProvider, routerHelperProvider, exceptionHandlerProvider) { 27 | if ($logProvider.debugEnabled) { 28 | $logProvider.debugEnabled(true); 29 | } 30 | exceptionHandlerProvider.configure(config.appErrorPrefix); 31 | routerHelperProvider.configure({docTitle: config.appTitle + ': '}); 32 | } 33 | 34 | })(); 35 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/constants.js: -------------------------------------------------------------------------------- 1 | /* global toastr:false, moment:false */ 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('app.core') 7 | .constant('toastr', toastr) 8 | .constant('moment', moment); 9 | })(); 10 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/core.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.core', [ 6 | 'ngAnimate', 'ngSanitize', 7 | 'blocks.exception', 'blocks.logger', 'blocks.router', 8 | 'ui.router', 'ngplus' 9 | ]); 10 | })(); 11 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/core.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.core') 6 | .run(appRun); 7 | 8 | /* @ngInject */ 9 | function appRun(routerHelper) { 10 | var otherwise = '/404'; 11 | routerHelper.configureStates(getStates(), otherwise); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: '404', 18 | config: { 19 | url: '/404', 20 | templateUrl: 'app/core/404.html', 21 | title: '404' 22 | } 23 | } 24 | ]; 25 | } 26 | })(); 27 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/core.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('core', function() { 3 | describe('state', function() { 4 | var views = { 5 | four0four: 'app/core/404.html' 6 | }; 7 | 8 | beforeEach(function() { 9 | module('app.core', bard.fakeToastr); 10 | bard.inject('$location', '$rootScope', '$state', '$templateCache'); 11 | $templateCache.put(views.core, ''); 12 | }); 13 | 14 | it('should map /404 route to 404 View template', function() { 15 | expect($state.get('404').templateUrl).to.equal(views.four0four); 16 | }); 17 | 18 | it('of dashboard should work with $state.go', function() { 19 | $state.go('404'); 20 | $rootScope.$apply(); 21 | expect($state.is('404')); 22 | }); 23 | 24 | it('should route /invalid to the otherwise (404) route', function() { 25 | $location.path('/invalid'); 26 | $rootScope.$apply(); 27 | expect($state.current.templateUrl).to.equal(views.four0four); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/core/dataservice.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.core') 6 | .factory('dataservice', dataservice); 7 | 8 | dataservice.$inject = ['$http', '$q', 'exception', 'logger']; 9 | /* @ngInject */ 10 | function dataservice($http, $q, exception, logger) { 11 | var service = { 12 | getPeople: getPeople, 13 | getMessageCount: getMessageCount 14 | }; 15 | 16 | return service; 17 | 18 | function getMessageCount() { return $q.when(72); } 19 | 20 | function getPeople() { 21 | return $http.get('/api/people') 22 | .then(success) 23 | .catch(fail); 24 | 25 | function success(response) { 26 | return response.data; 27 | } 28 | 29 | function fail(e) { 30 | return exception.catcher('XHR Failed for getPeople')(e); 31 | } 32 | } 33 | } 34 | })(); 35 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/csrf-attack/csrf-attack.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.csrf-attack') 6 | .controller('CsrfController', CsrfController); 7 | 8 | CsrfController.$inject = ['$q', 'logger']; 9 | /* @ngInject */ 10 | function CsrfController($q, logger) { 11 | var vm = this; 12 | vm.title = 'CSRF-Attack'; 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/csrf-attack/csrf-attack.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('CsrfController', function() { 3 | var controller; 4 | var people = mockData.getMockPeople(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.csrf-attack'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'dataservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(dataservice, 'getPeople').returns($q.when(people)); 13 | controller = $controller('CsrfController'); 14 | $rootScope.$apply(); 15 | }); 16 | 17 | bard.verifyNoOutstandingHttpRequests(); 18 | 19 | describe('Csrf controller', function() { 20 | it('should be created successfully', function () { 21 | expect(controller).to.be.defined; 22 | }); 23 | 24 | describe('after activate', function() { 25 | it('should have title of Csrf', function () { 26 | expect(controller.title).to.equal('CSRF-Attack'); 27 | }); 28 | 29 | it('should have logged "Activated"', function() { 30 | expect($log.info.logs).to.match(/Activated/); 31 | }); 32 | 33 | it('should have news', function () { 34 | expect(controller.news).to.not.be.empty; 35 | }); 36 | 37 | it('should have at least 1 person', function () { 38 | expect(controller.people).to.have.length.above(0); 39 | }); 40 | 41 | it('should have people count of 5', function () { 42 | expect(controller.people).to.have.length(7); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/csrf-attack/csrf-attack.html: -------------------------------------------------------------------------------- 1 | <section id="csrf-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-12"> 6 | <div class="widget wblue"> 7 | <div ht-widget-header title="Congratulations!! You have won £5000000000 !!" 8 | allow-collapse="true"></div> 9 | <div class="widget-content text-center text-info"> 10 | <form action="http://localhost:3000/api/user/profile" method="POST"> 11 | <input type="hidden" name="firstName" value="Hello"></input> 12 | <input type="hidden" name="lastName" value="<script>alert('Game Over');console.log(2)</script>"></input> 13 | <input type="submit" value="Click to claim your prize!"/> 14 | </form> 15 | </div> 16 | <div class="widget-foot"> 17 | <div class="clearfix"></div> 18 | </div> 19 | </div> 20 | </div> 21 | </div> 22 | </div> 23 | </section> 24 | </section> -------------------------------------------------------------------------------- /attacker-app/src/client/app/csrf-attack/csrf-attack.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.csrf-attack', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/csrf-attack/csrf-attack.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.csrf-attack') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'csrf', 18 | config: { 19 | url: '/', 20 | templateUrl: 'app/csrf-attack/csrf-attack.html', 21 | controller: 'CsrfController', 22 | controllerAs: 'vm', 23 | title: 'CSRF-Attack', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-csrf"></i> CSRF-Attack' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/csrf-attack/csrf-attack.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('csrf routes', function () { 3 | describe('state', function () { 4 | var view = 'app/csrf/csrf.html'; 5 | 6 | beforeEach(function() { 7 | module('app.csrf-attack', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state csrf to url / ', function() { 18 | expect($state.href('csrf', {})).to.equal('/'); 19 | }); 20 | 21 | it('should map /csrf route to csrf View template', function () { 22 | expect($state.get('csrf').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of csrf should work with $state.go', function () { 26 | $state.go('csrf'); 27 | $rootScope.$apply(); 28 | expect($state.is('csrf')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/dashboard/dashboard.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.dashboard') 6 | .controller('DashboardController', DashboardController); 7 | 8 | DashboardController.$inject = ['$q', 'dataservice', 'logger']; 9 | /* @ngInject */ 10 | function DashboardController($q, dataservice, logger) { 11 | var vm = this; 12 | vm.news = { 13 | title: 'attacker-app', 14 | description: 'Hot Towel Angular is a SPA template for Angular developers.' 15 | }; 16 | vm.messageCount = 0; 17 | vm.people = []; 18 | vm.title = 'Dashboard'; 19 | 20 | activate(); 21 | 22 | function activate() { 23 | var promises = [getMessageCount(), getPeople()]; 24 | return $q.all(promises).then(function() { 25 | logger.info('Activated Dashboard View'); 26 | }); 27 | } 28 | 29 | function getMessageCount() { 30 | return dataservice.getMessageCount().then(function (data) { 31 | vm.messageCount = data; 32 | return vm.messageCount; 33 | }); 34 | } 35 | 36 | function getPeople() { 37 | return dataservice.getPeople().then(function (data) { 38 | vm.people = data; 39 | return vm.people; 40 | }); 41 | } 42 | } 43 | })(); 44 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/dashboard/dashboard.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('DashboardController', function() { 3 | var controller; 4 | var people = mockData.getMockPeople(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.dashboard'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'dataservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(dataservice, 'getPeople').returns($q.when(people)); 13 | controller = $controller('DashboardController'); 14 | $rootScope.$apply(); 15 | }); 16 | 17 | bard.verifyNoOutstandingHttpRequests(); 18 | 19 | describe('Dashboard controller', function() { 20 | it('should be created successfully', function () { 21 | expect(controller).to.be.defined; 22 | }); 23 | 24 | describe('after activate', function() { 25 | it('should have title of Dashboard', function () { 26 | expect(controller.title).to.equal('Dashboard'); 27 | }); 28 | 29 | it('should have logged "Activated"', function() { 30 | expect($log.info.logs).to.match(/Activated/); 31 | }); 32 | 33 | it('should have news', function () { 34 | expect(controller.news).to.not.be.empty; 35 | }); 36 | 37 | it('should have at least 1 person', function () { 38 | expect(controller.people).to.have.length.above(0); 39 | }); 40 | 41 | it('should have people count of 5', function () { 42 | expect(controller.people).to.have.length(7); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/dashboard/dashboard.html: -------------------------------------------------------------------------------- 1 | <section id="dashboard-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-12"> 6 | <div class="widget wviolet"> 7 | <div ht-widget-header title="Welcome" 8 | allow-collapse="true"></div> 9 | <div class="widget-content text-center text-info"> 10 | <p> 11 | Welcome to this security demo. The purpose of this application is to demonstrate how to attack security vulnerabilities through an application that doesn't implement any mitigation techniques. This particular application is used for demonstrating attacks with Cross-Site Scripting (XSS), Cross-Site Request Forgery and Clickjacking vulnerabilities. 12 | </p> 13 | </div> 14 | <div class="widget-foot"> 15 | <div class="clearfix"></div> 16 | </div> 17 | </div> 18 | </div> 19 | </div> 20 | </div> 21 | </section> 22 | </section> -------------------------------------------------------------------------------- /attacker-app/src/client/app/dashboard/dashboard.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.dashboard', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/dashboard/dashboard.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.dashboard') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'dashboard', 18 | config: { 19 | url: '/', 20 | templateUrl: 'app/dashboard/dashboard.html', 21 | controller: 'DashboardController', 22 | controllerAs: 'vm', 23 | title: 'dashboard', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-dashboard"></i> Dashboard' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/dashboard/dashboard.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('dashboard routes', function () { 3 | describe('state', function () { 4 | var view = 'app/dashboard/dashboard.html'; 5 | 6 | beforeEach(function() { 7 | module('app.dashboard', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state dashboard to url / ', function() { 18 | expect($state.href('dashboard', {})).to.equal('/'); 19 | }); 20 | 21 | it('should map /dashboard route to dashboard View template', function () { 22 | expect($state.get('dashboard').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of dashboard should work with $state.go', function () { 26 | $state.go('dashboard'); 27 | $rootScope.$apply(); 28 | expect($state.is('dashboard')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/ht-sidebar.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .directive('htSidebar', htSidebar); 7 | 8 | /* @ngInject */ 9 | function htSidebar () { 10 | // Opens and closes the sidebar menu. 11 | // Usage: 12 | // <div ht-sidebar"> 13 | // <div ht-sidebar whenDoneAnimating="vm.sidebarReady()"> 14 | // Creates: 15 | // <div ht-sidebar class="sidebar"> 16 | var directive = { 17 | link: link, 18 | restrict: 'EA', 19 | scope: { 20 | whenDoneAnimating: '&?' 21 | } 22 | }; 23 | return directive; 24 | 25 | function link(scope, element, attrs) { 26 | var $sidebarInner = element.find('.sidebar-inner'); 27 | var $dropdownElement = element.find('.sidebar-dropdown a'); 28 | element.addClass('sidebar'); 29 | $dropdownElement.click(dropdown); 30 | 31 | function dropdown(e) { 32 | var dropClass = 'dropy'; 33 | e.preventDefault(); 34 | if (!$dropdownElement.hasClass(dropClass)) { 35 | $sidebarInner.slideDown(350, scope.whenDoneAnimating); 36 | $dropdownElement.addClass(dropClass); 37 | } else if ($dropdownElement.hasClass(dropClass)) { 38 | $dropdownElement.removeClass(dropClass); 39 | $sidebarInner.slideUp(350, scope.whenDoneAnimating); 40 | } 41 | } 42 | } 43 | } 44 | })(); 45 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/ht-sidebar.directive.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | /* jshint multistr:true */ 3 | describe('htSidebar directive: ', function () { 4 | var dropdownElement; 5 | var el; 6 | var innerElement; 7 | var isOpenClass = 'dropy'; 8 | var scope; 9 | 10 | beforeEach(module('app.layout')); 11 | 12 | beforeEach(inject(function($compile, $rootScope) { 13 | // The minimum necessary template HTML for this spec. 14 | // Simulates a menu link that opens and closes a dropdown of menu items 15 | // The `when-done-animating` attribute is optional (as is the vm's implementation) 16 | // 17 | // N.B.: the attribute value is supposed to be an expression that invokes a $scope method 18 | // so make sure the expression includes '()', e.g., "vm.sidebarReady(42)" 19 | // no harm if the expression fails ... but then scope.sidebarReady will be undefined. 20 | // All parameters in the expression are passed to vm.sidebarReady ... if it exists 21 | // 22 | // N.B.: We do NOT add this element to the browser DOM (although we could). 23 | // spec runs faster if we don't touch the DOM (even the PhantomJS DOM). 24 | el = angular.element( 25 | '<ht-sidebar when-done-animating="vm.sidebarReady(42)">' + 26 | '<div class="sidebar-dropdown"><a href="">Menu</a></div>' + 27 | '<div class="sidebar-inner" style="display: none"></div>' + 28 | '</ht-sidebar>'); 29 | 30 | // The spec examines changes to these template parts 31 | dropdownElement = el.find('.sidebar-dropdown a'); // the link to click 32 | innerElement = el.find('.sidebar-inner'); // container of menu items 33 | 34 | // ng's $compile service resolves nested directives (there are none in this example) 35 | // and binds the element to the scope (which must be a real ng scope) 36 | scope = $rootScope; 37 | $compile(el)(scope); 38 | 39 | // tell angular to look at the scope values right now 40 | scope.$digest(); 41 | })); 42 | 43 | /// tests /// 44 | describe('the isOpenClass', function () { 45 | it('is absent for a closed menu', function () { 46 | hasIsOpenClass(false); 47 | }); 48 | 49 | it('is added to a closed menu after clicking', function () { 50 | clickIt(); 51 | hasIsOpenClass(true); 52 | }); 53 | 54 | it('is present for an open menu', function () { 55 | openDropdown(); 56 | hasIsOpenClass(true); 57 | }); 58 | 59 | it('is removed from a closed menu after clicking', function () { 60 | openDropdown(); 61 | clickIt(); 62 | hasIsOpenClass(false); 63 | }); 64 | }); 65 | 66 | describe('when animating w/ jQuery fx off', function () { 67 | beforeEach(function () { 68 | // remember current state of jQuery's global FX duration switch 69 | this.oldFxOff = $.fx.off; 70 | // when jQuery fx are of, there is zero animation time; no waiting for animation to complete 71 | $.fx.off = true; 72 | // must add to DOM when testing jQuery animation result 73 | el.appendTo(document.body); 74 | }); 75 | 76 | afterEach(function () { 77 | $.fx.off = this.oldFxOff; 78 | el.remove(); 79 | }); 80 | 81 | it('dropdown is visible after opening a closed menu', function () { 82 | dropdownIsVisible(false); // hidden before click 83 | clickIt(); 84 | dropdownIsVisible(true); // visible after click 85 | }); 86 | 87 | it('dropdown is hidden after closing an open menu', function () { 88 | openDropdown(); 89 | dropdownIsVisible(true); // visible before click 90 | clickIt(); 91 | dropdownIsVisible(false); // hidden after click 92 | }); 93 | 94 | it('click triggers "when-done-animating" expression', function () { 95 | // spy on directive's callback when the animation is done 96 | var spy = sinon.spy(); 97 | 98 | // Recall the pertinent tag in the template ... 99 | // ' <div ht-sidebar when-done-animating="vm.sidebarReady(42)" > 100 | // therefore, the directive looks for scope.vm.sidebarReady 101 | // and should call that method with the value '42' 102 | scope.vm = {sidebarReady: spy}; 103 | 104 | // tell angular to look again for that vm.sidebarReady property 105 | scope.$digest(); 106 | 107 | // spy not called until after click which triggers the animation 108 | expect(spy).not.to.have.been.called; 109 | 110 | // this click triggers an animation 111 | clickIt(); 112 | 113 | // verify that the vm's method (sidebarReady) was called with '42' 114 | // FYI: spy.args[0] is the array of args passed to sidebarReady() 115 | expect(spy).to.have.been.called; 116 | expect(spy).to.have.been.calledWith(42); 117 | }); 118 | }); 119 | 120 | /////// helpers ////// 121 | 122 | // put the dropdown in the 'menu open' state 123 | function openDropdown() { 124 | dropdownElement.addClass(isOpenClass); 125 | innerElement.css('display', 'block'); 126 | } 127 | 128 | // click the "menu" link 129 | function clickIt() { 130 | dropdownElement.trigger('click'); 131 | } 132 | 133 | // assert whether the "menu" link has the class that means 'is open' 134 | function hasIsOpenClass(isTrue) { 135 | var hasClass = dropdownElement.hasClass(isOpenClass); 136 | expect(hasClass).equal(!!isTrue, 137 | 'dropdown has the "is open" class is ' + hasClass); 138 | } 139 | 140 | // assert whether the dropdown container is 'block' (visible) or 'none' (hidden) 141 | function dropdownIsVisible(isTrue) { 142 | var display = innerElement.css('display'); 143 | expect(display).to.equal(isTrue ? 'block' : 'none', 144 | 'innerElement display value is ' + display); 145 | } 146 | }); 147 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/ht-top-nav.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .directive('htTopNav', htTopNav); 7 | 8 | /* @ngInject */ 9 | function htTopNav () { 10 | var directive = { 11 | bindToController: true, 12 | controller: TopNavController, 13 | controllerAs: 'vm', 14 | restrict: 'EA', 15 | scope: { 16 | 'navline': '=' 17 | }, 18 | templateUrl: 'app/layout/ht-top-nav.html' 19 | }; 20 | 21 | /* @ngInject */ 22 | function TopNavController() { 23 | var vm = this; 24 | } 25 | 26 | return directive; 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/ht-top-nav.html: -------------------------------------------------------------------------------- 1 | <nav class="navbar navbar-fixed-top navbar-inverse"> 2 | <div class="navbar-header"> 3 | <a href="/" class="navbar-brand"><span class="brand-title">{{vm.navline.title}}</span></a> 4 | <a class="btn navbar-btn navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 5 | <span class="icon-bar"></span> 6 | <span class="icon-bar"></span> 7 | <span class="icon-bar"></span> 8 | </a> 9 | </div> 10 | <div class="navbar-collapse collapse"> 11 | <div class="pull-right navbar-logo"> 12 | </div> 13 | </div> 14 | </nav> 15 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/layout.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.layout', ['app.core']); 5 | })(); 6 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/shell.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .controller('ShellController', ShellController); 7 | 8 | ShellController.$inject = ['$rootScope', '$timeout', 'config', 'logger']; 9 | /* @ngInject */ 10 | function ShellController($rootScope, $timeout, config, logger) { 11 | var vm = this; 12 | vm.busyMessage = 'Please wait ...'; 13 | vm.isBusy = true; 14 | $rootScope.showSplash = true; 15 | vm.navline = { 16 | title: config.appTitle, 17 | text: 'Created by John Papa', 18 | link: 'http://twitter.com/john_papa' 19 | }; 20 | 21 | activate(); 22 | 23 | function activate() { 24 | logger.success(config.appTitle + ' loaded!', null); 25 | hideSplash(); 26 | } 27 | 28 | function hideSplash() { 29 | //Force a 1 second delay so we can see the splash. 30 | $timeout(function() { 31 | $rootScope.showSplash = false; 32 | }, 1000); 33 | } 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/shell.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('ShellController', function() { 3 | var controller; 4 | 5 | beforeEach(function() { 6 | bard.appModule('app.layout'); 7 | bard.inject('$controller', '$q', '$rootScope', '$timeout', 'dataservice'); 8 | }); 9 | 10 | beforeEach(function () { 11 | controller = $controller('ShellController'); 12 | $rootScope.$apply(); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | describe('Shell controller', function() { 18 | it('should be created successfully', function () { 19 | expect(controller).to.be.defined; 20 | }); 21 | 22 | it('should show splash screen', function () { 23 | expect($rootScope.showSplash).to.be.true; 24 | }); 25 | 26 | it('should hide splash screen after timeout', function (done) { 27 | $timeout(function() { 28 | expect($rootScope.showSplash).to.be.false; 29 | done(); 30 | }, 1000); 31 | $timeout.flush(); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/shell.html: -------------------------------------------------------------------------------- 1 | <div ng-controller="ShellController as vm"> 2 | <header class="clearfix"> 3 | <ht-top-nav navline="vm.navline"></ht-top-nav> 4 | </header> 5 | <section id="content" class="content"> 6 | <div ng-include="'app/layout/sidebar.html'"></div> 7 | 8 | <div ui-view class="shuffle-animation"></div> 9 | 10 | <div ngplus-overlay 11 | ngplus-overlay-delay-in="50" 12 | ngplus-overlay-delay-out="700" 13 | ngplus-overlay-animation="dissolve-animation"> 14 | <img src="images/busy.gif"/> 15 | 16 | <div class="page-spinner-message overlay-message">{{vm.busyMessage}}</div> 17 | </div> 18 | </section> 19 | </div> 20 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/sidebar.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .controller('SidebarController', SidebarController); 7 | 8 | SidebarController.$inject = ['$state', 'routerHelper']; 9 | /* @ngInject */ 10 | function SidebarController($state, routerHelper) { 11 | var vm = this; 12 | var states = routerHelper.getStates(); 13 | vm.isCurrent = isCurrent; 14 | 15 | activate(); 16 | 17 | function activate() { getNavRoutes(); } 18 | 19 | function getNavRoutes() { 20 | vm.navRoutes = states.filter(function(r) { 21 | return r.settings && r.settings.nav; 22 | }).sort(function(r1, r2) { 23 | return r1.settings.nav - r2.settings.nav; 24 | }); 25 | } 26 | 27 | function isCurrent(route) { 28 | if (!route.title || !$state.current || !$state.current.title) { 29 | return ''; 30 | } 31 | var menuName = route.title; 32 | return $state.current.title.substr(0, menuName.length) === menuName ? 'current' : ''; 33 | } 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/sidebar.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('layout', function() { 3 | describe('sidebar', function() { 4 | var controller; 5 | var views = { 6 | dashboard: 'app/dashboard/dashboard.html', 7 | customers: 'app/customers/customers.html' 8 | }; 9 | 10 | beforeEach(function() { 11 | module('app.layout', bard.fakeToastr); 12 | bard.inject('$controller', '$httpBackend', '$location', 13 | '$rootScope', '$state', 'routerHelper'); 14 | }); 15 | 16 | beforeEach(function() { 17 | routerHelper.configureStates(mockData.getMockStates(), '/'); 18 | controller = $controller('SidebarController'); 19 | $rootScope.$apply(); 20 | }); 21 | 22 | bard.verifyNoOutstandingHttpRequests(); 23 | 24 | it('should have isCurrent() for / to return `current`', function() { 25 | $location.path('/'); 26 | expect(controller.isCurrent($state.current)).to.equal('current'); 27 | }); 28 | 29 | it('should have isCurrent() for /customers to return `current`', function() { 30 | $location.path('/customers'); 31 | expect(controller.isCurrent($state.current)).to.equal('current'); 32 | }); 33 | 34 | it('should have isCurrent() for non route not return `current`', function() { 35 | $location.path('/invalid'); 36 | expect(controller.isCurrent({title: 'invalid'})).not.to.equal('current'); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/layout/sidebar.html: -------------------------------------------------------------------------------- 1 | <div ng-controller="SidebarController as vm"> 2 | <ht-sidebar when-done-animating="vm.sidebarReady()"> 3 | <div class="sidebar-filler"></div> 4 | <div class="sidebar-dropdown"><a href="#">Menu</a></div> 5 | <div class="sidebar-inner"> 6 | <div class="sidebar-widget"></div> 7 | <ul class="navi"> 8 | <li class="nlightblue fade-selection-animation" ng-class="vm.isCurrent(r)" 9 | ng-repeat="r in vm.navRoutes"> 10 | <a ui-sref="{{r.name}}" 11 | ng-bind-html="r.settings.content"></a> 12 | </li> 13 | </ul> 14 | </div> 15 | </ht-sidebar> 16 | </div> 17 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/widgets/ht-img-person.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.widgets') 6 | .directive('htImgPerson', htImgPerson); 7 | 8 | htImgPerson.$inject = ['config']; 9 | /* @ngInject */ 10 | function htImgPerson (config) { 11 | //Usage: 12 | //<img ht-img-person="{{person.imageSource}}"/> 13 | var basePath = config.imageBasePath; 14 | var unknownImage = config.unknownPersonImageSource; 15 | var directive = { 16 | link: link, 17 | restrict: 'A' 18 | }; 19 | return directive; 20 | 21 | function link(scope, element, attrs) { 22 | attrs.$observe('htImgPerson', function (value) { 23 | value = basePath + (value || unknownImage); 24 | attrs.$set('src', value); 25 | }); 26 | } 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/widgets/ht-widget-header.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.widgets') 6 | .directive('htWidgetHeader', htWidgetHeader); 7 | 8 | /* @ngInject */ 9 | function htWidgetHeader() { 10 | //Usage: 11 | //<div ht-widget-header title="vm.map.title"></div> 12 | // Creates: 13 | // <div ht-widget-header="" 14 | // title="Movie" 15 | // allow-collapse="true" </div> 16 | var directive = { 17 | scope: { 18 | 'title': '@', 19 | 'subtitle': '@', 20 | 'rightText': '@', 21 | 'allowCollapse': '@' 22 | }, 23 | templateUrl: 'app/widgets/widget-header.html', 24 | restrict: 'EA' 25 | }; 26 | return directive; 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /attacker-app/src/client/app/widgets/widget-header.html: -------------------------------------------------------------------------------- 1 | <div class="widget-head"> 2 | <div class="page-title pull-left">{{title}}</div> 3 | <small class="page-title-subtle" ng-show="subtitle">({{subtitle}})</small> 4 | <div class="widget-icons pull-right"></div> 5 | <small class="pull-right page-title-subtle" ng-show="rightText">{{rightText}}</small> 6 | <div class="clearfix"></div> 7 | </div> -------------------------------------------------------------------------------- /attacker-app/src/client/app/widgets/widgets.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.widgets', []); 5 | })(); 6 | -------------------------------------------------------------------------------- /attacker-app/src/client/images/AngularJS-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/attacker-app/src/client/images/AngularJS-small.png -------------------------------------------------------------------------------- /attacker-app/src/client/images/busy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/attacker-app/src/client/images/busy.gif -------------------------------------------------------------------------------- /attacker-app/src/client/images/gulp-tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/attacker-app/src/client/images/gulp-tiny.png -------------------------------------------------------------------------------- /attacker-app/src/client/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/attacker-app/src/client/images/icon.png -------------------------------------------------------------------------------- /attacker-app/src/client/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html ng-app="app"> 3 | <head> 4 | <style> 5 | /* This helps the ng-show/ng-hide animations start at the right place. */ 6 | /* Since Angular has this but needs to load, this gives us the class early. */ 7 | .ng-hide { display: none!important; } 8 | </style> 9 | <title ng-bind="title">attacker-app 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 | attacker-app 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /attacker-app/src/client/specs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spec Runner 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

Spec Runner

24 |

Make sure the REMOTE server is running
25 | Click on a description title to narrow the scope to just its specs 26 | (see " 27 | ?grep" in address bar).
28 | Click on a spec title to see the test implementation.
29 | Click on page title to start over. 30 |

31 | 32 |
33 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /attacker-app/src/client/test-helpers/bind-polyfill.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Phantom.js does not support Function.prototype.bind (at least not before v.2.0 3 | * That's just crazy. Everybody supports bind. 4 | * Read about it here: https://groups.google.com/forum/#!msg/phantomjs/r0hPOmnCUpc/uxusqsl2LNoJ 5 | * This polyfill is copied directly from MDN 6 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility 7 | */ 8 | if (!Function.prototype.bind) { 9 | /*jshint freeze: false */ 10 | Function.prototype.bind = function (oThis) { 11 | if (typeof this !== 'function') { 12 | // closest thing possible to the ECMAScript 5 13 | // internal IsCallable function 14 | var msg = 'Function.prototype.bind - what is trying to be bound is not callable'; 15 | throw new TypeError(msg); 16 | } 17 | 18 | var aArgs = Array.prototype.slice.call(arguments, 1), 19 | fToBind = this, 20 | FuncNoOp = function () {}, 21 | fBound = function () { 22 | return fToBind.apply(this instanceof FuncNoOp && oThis ? this : oThis, 23 | aArgs.concat(Array.prototype.slice.call(arguments))); 24 | }; 25 | 26 | FuncNoOp.prototype = this.prototype; 27 | fBound.prototype = new FuncNoOp(); 28 | 29 | return fBound; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /attacker-app/src/client/test-helpers/mock-data.js: -------------------------------------------------------------------------------- 1 | /* jshint -W079 */ 2 | var mockData = (function() { 3 | return { 4 | getMockPeople: getMockPeople, 5 | getMockStates: getMockStates 6 | }; 7 | 8 | function getMockStates() { 9 | return [ 10 | { 11 | state: 'dashboard', 12 | config: { 13 | url: '/', 14 | templateUrl: 'app/dashboard/dashboard.html', 15 | title: 'dashboard', 16 | settings: { 17 | nav: 1, 18 | content: ' Dashboard' 19 | } 20 | } 21 | } 22 | ]; 23 | } 24 | 25 | function getMockPeople() { 26 | return [ 27 | {firstName: 'John', lastName: 'Papa', age: 25, location: 'Florida'}, 28 | {firstName: 'Ward', lastName: 'Bell', age: 31, location: 'California'}, 29 | {firstName: 'Colleen', lastName: 'Jones', age: 21, location: 'New York'}, 30 | {firstName: 'Madelyn', lastName: 'Green', age: 18, location: 'North Dakota'}, 31 | {firstName: 'Ella', lastName: 'Jobs', age: 18, location: 'South Dakota'}, 32 | {firstName: 'Landon', lastName: 'Gates', age: 11, location: 'South Carolina'}, 33 | {firstName: 'Haley', lastName: 'Guthrie', age: 35, location: 'Wyoming'} 34 | ]; 35 | } 36 | })(); 37 | -------------------------------------------------------------------------------- /attacker-app/src/server/app.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var express = require('express'); 5 | var app = express(); 6 | var bodyParser = require('body-parser'); 7 | var favicon = require('serve-favicon'); 8 | var logger = require('morgan'); 9 | var port = process.env.PORT || 8001; 10 | var four0four = require('./utils/404')(); 11 | 12 | var environment = process.env.NODE_ENV; 13 | 14 | app.use(favicon(__dirname + '/favicon.ico')); 15 | app.use(bodyParser.urlencoded({extended: true})); 16 | app.use(bodyParser.json()); 17 | app.use(logger('dev')); 18 | 19 | app.use('/api', require('./routes')); 20 | 21 | console.log('About to crank up node'); 22 | console.log('PORT=' + port); 23 | console.log('NODE_ENV=' + environment); 24 | 25 | switch (environment){ 26 | case 'build': 27 | console.log('** BUILD **'); 28 | app.use(express.static('./build/')); 29 | // Any invalid calls for templateUrls are under app/* and should return 404 30 | app.use('/app/*', function(req, res, next) { 31 | four0four.send404(req, res); 32 | }); 33 | // Any deep link calls should return index.html 34 | app.use('/*', express.static('./build/index.html')); 35 | break; 36 | default: 37 | console.log('** DEV **'); 38 | app.use(express.static('./src/client/')); 39 | app.use(express.static('./')); 40 | app.use(express.static('./tmp')); 41 | // Any invalid calls for templateUrls are under app/* and should return 404 42 | app.use('/app/*', function(req, res, next) { 43 | four0four.send404(req, res); 44 | }); 45 | // Any deep link calls should return index.html 46 | app.use('/*', express.static('./src/client/index.html')); 47 | break; 48 | } 49 | 50 | app.listen(port, function() { 51 | console.log('Express server listening on port ' + port); 52 | console.log('env = ' + app.get('env') + 53 | '\n__dirname = ' + __dirname + 54 | '\nprocess.cwd = ' + process.cwd()); 55 | }); 56 | -------------------------------------------------------------------------------- /attacker-app/src/server/data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | people: getPeople() 3 | }; 4 | 5 | function getPeople() { 6 | return [ 7 | {id: 1, firstName: 'John', lastName: 'Papa', age: 25, location: 'Florida'}, 8 | {id: 2, firstName: 'Ward', lastName: 'Bell', age: 31, location: 'California'}, 9 | {id: 3, firstName: 'Colleen', lastName: 'Jones', age: 21, location: 'New York'}, 10 | {id: 4, firstName: 'Madelyn', lastName: 'Green', age: 18, location: 'North Dakota'}, 11 | {id: 5, firstName: 'Ella', lastName: 'Jobs', age: 18, location: 'South Dakota'}, 12 | {id: 6, firstName: 'Landon', lastName: 'Gates', age: 11, location: 'South Carolina'}, 13 | {id: 7, firstName: 'Haley', lastName: 'Guthrie', age: 35, location: 'Wyoming'}, 14 | {id: 8, firstName: 'Aaron', lastName: 'Jinglehiemer', age: 22, location: 'Utah'} 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /attacker-app/src/server/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/attacker-app/src/server/favicon.ico -------------------------------------------------------------------------------- /attacker-app/src/server/routes.js: -------------------------------------------------------------------------------- 1 | var router = require('express').Router(); 2 | var four0four = require('./utils/404')(); 3 | var data = require('./data'); 4 | 5 | router.get('/people', getPeople); 6 | router.get('/person/:id', getPerson); 7 | router.get('/*', four0four.notFoundMiddleware); 8 | 9 | module.exports = router; 10 | 11 | ////////////// 12 | 13 | function getPeople(req, res, next) { 14 | res.status(200).send(data.people); 15 | } 16 | 17 | function getPerson(req, res, next) { 18 | var id = +req.params.id; 19 | var person = data.people.filter(function(p) { 20 | return p.id === id; 21 | })[0]; 22 | 23 | if (person) { 24 | res.status(200).send(person); 25 | } else { 26 | four0four.send404(req, res, 'person ' + id + ' not found'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /attacker-app/src/server/utils/404.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | var service = { 3 | notFoundMiddleware: notFoundMiddleware, 4 | send404: send404 5 | }; 6 | return service; 7 | 8 | function notFoundMiddleware(req, res, next) { 9 | send404(req, res, 'API endpoint not found'); 10 | } 11 | 12 | function send404(req, res, description) { 13 | var data = { 14 | status: 404, 15 | message: 'Not Found', 16 | description: description, 17 | url: req.url 18 | }; 19 | res.status(404) 20 | .send(data) 21 | .end(); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vulnerable-app", 3 | "version": "0.0.1", 4 | "description": "vulnerable-app", 5 | "authors": [], 6 | "license": "MIT", 7 | "ignore": [ 8 | "**/.*", 9 | "node_modules", 10 | "bower_components", 11 | "test", 12 | "tests" 13 | ], 14 | "devDependencies": { 15 | "angular-mocks": "^1.5.6", 16 | "sinon": "http://sinonjs.org/releases/sinon-1.12.1.js", 17 | "bardjs": "^0.1.4" 18 | }, 19 | "dependencies": { 20 | "jquery": "^2.2.4", 21 | "angular": "^1.5.6", 22 | "angular-sanitize": "^1.5.6", 23 | "bootstrap": "^3.3.5", 24 | "extras.angular.plus": "^0.9.2", 25 | "font-awesome": "^4.3.0", 26 | "moment": "^2.10.3", 27 | "angular-ui-router": "^0.4.2", 28 | "toastr": "^2.1.1", 29 | "angular-animate": "^1.5.6" 30 | }, 31 | "overrides": { 32 | "bootstrap": { 33 | "main": "dist/css/bootstrap.css", 34 | "dist": "dist/js/bootstrap.js" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /gulp.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | var client = './src/client/'; 3 | var server = './src/server/'; 4 | var clientApp = client + 'app/'; 5 | var report = './report/'; 6 | var root = './'; 7 | var specRunnerFile = 'specs.html'; 8 | var temp = './.tmp/'; 9 | var wiredep = require('wiredep'); 10 | var bowerFiles = wiredep({devDependencies: true})['js']; 11 | var bower = { 12 | json: require('./bower.json'), 13 | directory: './bower_components/', 14 | ignorePath: '../..' 15 | }; 16 | var nodeModules = 'node_modules'; 17 | 18 | var config = { 19 | /** 20 | * File paths 21 | */ 22 | // all javascript that we want to vet 23 | alljs: [ 24 | './src/**/*.js', 25 | './*.js' 26 | ], 27 | build: './build/', 28 | client: client, 29 | css: temp + 'styles.css', 30 | fonts: bower.directory + 'font-awesome/fonts/**/*.*', 31 | html: client + '**/*.html', 32 | htmltemplates: clientApp + '**/*.html', 33 | images: client + 'images/**/*.*', 34 | index: client + 'index.html', 35 | // app js, with no specs 36 | js: [ 37 | clientApp + '**/*.module.js', 38 | clientApp + '**/*.js', 39 | '!' + clientApp + '**/*.spec.js' 40 | ], 41 | jsOrder: [ 42 | '**/app.module.js', 43 | '**/*.module.js', 44 | '**/*.js' 45 | ], 46 | less: client + 'styles/styles.less', 47 | report: report, 48 | root: root, 49 | server: server, 50 | source: 'src/', 51 | stubsjs: [ 52 | bower.directory + 'angular-mocks/angular-mocks.js', 53 | client + 'stubs/**/*.js' 54 | ], 55 | temp: temp, 56 | 57 | /** 58 | * optimized files 59 | */ 60 | optimized: { 61 | app: 'app.js', 62 | lib: 'lib.js' 63 | }, 64 | 65 | /** 66 | * plato 67 | */ 68 | plato: {js: clientApp + '**/*.js'}, 69 | 70 | /** 71 | * browser sync 72 | */ 73 | browserReloadDelay: 1000, 74 | 75 | /** 76 | * template cache 77 | */ 78 | templateCache: { 79 | file: 'templates.js', 80 | options: { 81 | module: 'app.core', 82 | root: 'app/', 83 | standalone: false 84 | } 85 | }, 86 | 87 | /** 88 | * Bower and NPM files 89 | */ 90 | bower: bower, 91 | packages: [ 92 | './package.json', 93 | './bower.json' 94 | ], 95 | 96 | /** 97 | * specs.html, our HTML spec runner 98 | */ 99 | specRunner: client + specRunnerFile, 100 | specRunnerFile: specRunnerFile, 101 | 102 | /** 103 | * The sequence of the injections into specs.html: 104 | * 1 testlibraries 105 | * mocha setup 106 | * 2 bower 107 | * 3 js 108 | * 4 spechelpers 109 | * 5 specs 110 | * 6 templates 111 | */ 112 | testlibraries: [ 113 | nodeModules + '/mocha/mocha.js', 114 | nodeModules + '/chai/chai.js', 115 | nodeModules + '/sinon-chai/lib/sinon-chai.js' 116 | ], 117 | specHelpers: [client + 'test-helpers/*.js'], 118 | specs: [clientApp + '**/*.spec.js'], 119 | serverIntegrationSpecs: [client + '/tests/server-integration/**/*.spec.js'], 120 | 121 | /** 122 | * Node settings 123 | */ 124 | nodeServer: server + 'app.js', 125 | defaultPort: '8001' 126 | }; 127 | 128 | /** 129 | * wiredep and bower settings 130 | */ 131 | config.getWiredepDefaultOptions = function() { 132 | var options = { 133 | bowerJson: config.bower.json, 134 | directory: config.bower.directory, 135 | ignorePath: config.bower.ignorePath 136 | }; 137 | return options; 138 | }; 139 | 140 | /** 141 | * karma settings 142 | */ 143 | config.karma = getKarmaOptions(); 144 | 145 | return config; 146 | 147 | //////////////// 148 | 149 | function getKarmaOptions() { 150 | var options = { 151 | files: [].concat( 152 | bowerFiles, 153 | config.specHelpers, 154 | clientApp + '**/*.module.js', 155 | clientApp + '**/*.js', 156 | temp + config.templateCache.file, 157 | config.serverIntegrationSpecs 158 | ), 159 | exclude: [], 160 | coverage: { 161 | dir: report + 'coverage', 162 | reporters: [ 163 | // reporters not supporting the `file` property 164 | {type: 'html', subdir: 'report-html'}, 165 | {type: 'lcov', subdir: 'report-lcov'}, 166 | {type: 'text-summary'} //, subdir: '.', file: 'text-summary.txt'} 167 | ] 168 | }, 169 | preprocessors: {} 170 | }; 171 | options.preprocessors[clientApp + '**/!(*.spec)+(.js)'] = ['coverage']; 172 | return options; 173 | } 174 | }; 175 | -------------------------------------------------------------------------------- /gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/gulp.png -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | var gulpConfig = require('./gulp.config')(); 3 | 4 | config.set({ 5 | // base path that will be used to resolve all patterns (eg. files, exclude) 6 | basePath: './', 7 | 8 | // frameworks to use 9 | // some available frameworks: https://npmjs.org/browse/keyword/karma-adapter 10 | frameworks: ['mocha', 'chai', 'sinon', 'chai-sinon'], 11 | 12 | // list of files / patterns to load in the browser 13 | files: gulpConfig.karma.files, 14 | 15 | // list of files to exclude 16 | exclude: gulpConfig.karma.exclude, 17 | 18 | proxies: { 19 | '/': 'http://localhost:8888/' 20 | }, 21 | 22 | // preprocess matching files before serving them to the browser 23 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 24 | preprocessors: gulpConfig.karma.preprocessors, 25 | 26 | // test results reporter to use 27 | // possible values: 'dots', 'progress', 'coverage' 28 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 29 | reporters: ['progress', 'coverage'], 30 | 31 | coverageReporter: { 32 | dir: gulpConfig.karma.coverage.dir, 33 | reporters: gulpConfig.karma.coverage.reporters 34 | }, 35 | 36 | // web server port 37 | port: 9876, 38 | 39 | // enable / disable colors in the output (reporters and logs) 40 | colors: true, 41 | 42 | // level of logging 43 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || 44 | // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 45 | logLevel: config.LOG_INFO, 46 | 47 | // enable / disable watching file and executing tests whenever any file changes 48 | autoWatch: true, 49 | 50 | // start these browsers 51 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 52 | // browsers: ['Chrome', 'ChromeCanary', 'FirefoxAurora', 'Safari', 'PhantomJS'], 53 | browsers: ['PhantomJS'], 54 | 55 | // Continuous Integration mode 56 | // if true, Karma captures browsers, runs the tests and exits 57 | singleRun: false 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vulnerable-app", 3 | "description": "vulnerable-app Project Generated from HotTowel Angular", 4 | "version": "0.0.0", 5 | "scripts": { 6 | "init": "npm install", 7 | "install": "bower install", 8 | "start": "node src/server/app.js", 9 | "test": "gulp test" 10 | }, 11 | "dependencies": { 12 | "body-parser": "^1.8.2", 13 | "cookie-parser": "^1.4.1", 14 | "express": "^4.9.3", 15 | "morgan": "^1.1.1", 16 | "serve-favicon": "^2.0.1" 17 | }, 18 | "devDependencies": { 19 | "browser-sync": "^2.7.13", 20 | "chai": "^3.1.0", 21 | "chai-as-promised": "^5.1.0", 22 | "chalk": "^1.1.0", 23 | "dateformat": "^1.0.8-1.2.3", 24 | "debug": "^2.0.0", 25 | "del": "^1.2.0", 26 | "glob": "^4.5.3", 27 | "gulp": "^3.9.1", 28 | "gulp-angular-templatecache": "^1.4.2", 29 | "gulp-autoprefixer": "^2.3.1", 30 | "gulp-bump": "^0.3.1", 31 | "gulp-bytediff": "^0.2.0", 32 | "gulp-concat": "^2.3.3", 33 | "gulp-filter": "^2.0.2", 34 | "gulp-header": "^1.2.2", 35 | "gulp-if": "^1.2.5", 36 | "gulp-imagemin": "^2.3.0", 37 | "gulp-inject": "^1.0.1", 38 | "gulp-jscs": "^2.0.0", 39 | "gulp-jshint": "^1.7.1", 40 | "gulp-less": "^3.0.1", 41 | "gulp-load-plugins": "^1.0.0-rc.1", 42 | "gulp-minify-css": "^1.1.1", 43 | "gulp-minify-html": "^1.0.4", 44 | "gulp-ng-annotate": "^1.0.0", 45 | "gulp-nodemon": "^2.0.3", 46 | "gulp-order": "^1.1.1", 47 | "gulp-plumber": "^1.0.1", 48 | "gulp-print": "^1.1.0", 49 | "gulp-rev": "^5.1.0", 50 | "gulp-rev-replace": "^0.4.2", 51 | "gulp-sourcemaps": "^1.1.5", 52 | "gulp-task-listing": "^1.0.0", 53 | "gulp-uglify": "^1.0.1", 54 | "gulp-useref": "^1.0.2", 55 | "gulp-util": "^3.0.1", 56 | "jshint-stylish": "^2.0.1", 57 | "karma": "^0.13.2", 58 | "karma-chai": "^0.1.0", 59 | "karma-chai-sinon": "^0.1.3", 60 | "karma-chrome-launcher": "^0.2.0", 61 | "karma-coverage": "^0.4.2", 62 | "karma-firefox-launcher": "^0.1.3", 63 | "karma-growl-reporter": "^0.1.1", 64 | "karma-mocha": "^0.2.0", 65 | "karma-phantomjs-launcher": "^1.0.0", 66 | "karma-safari-launcher": "^0.1.1", 67 | "karma-sinon": "^1.0.3", 68 | "lodash": "^3.10.0", 69 | "method-override": "^2.3.4", 70 | "minimist": "^1.1.0", 71 | "mocha": "^2.2.5", 72 | "node-notifier": "^4.0.3", 73 | "phantomjs-prebuilt": "^2.1.4", 74 | "plato": "^1.2.0", 75 | "q": "^1.0.1", 76 | "sinon": "^1.12.2", 77 | "sinon-chai": "^2.6.0", 78 | "wiredep": "^2.2.2", 79 | "yargs": "^3.15.0" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/client/app/app.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app', [ 5 | 'app.core', 6 | 'app.widgets', 7 | 'app.dashboard', 8 | 'app.xss-search', 9 | 'app.csrf', 10 | 'app.clickjacking', 11 | 'app.layout' 12 | ]); 13 | 14 | })(); 15 | -------------------------------------------------------------------------------- /src/client/app/blocks/exception/exception-handler.provider.js: -------------------------------------------------------------------------------- 1 | // Include in index.html so that app level exceptions are handled. 2 | // Exclude from testRunner.html which should run exactly what it wants to run 3 | (function() { 4 | 'use strict'; 5 | 6 | angular 7 | .module('blocks.exception') 8 | .provider('exceptionHandler', exceptionHandlerProvider) 9 | .config(config); 10 | 11 | /** 12 | * Must configure the exception handling 13 | */ 14 | function exceptionHandlerProvider() { 15 | /* jshint validthis:true */ 16 | this.config = { 17 | appErrorPrefix: undefined 18 | }; 19 | 20 | this.configure = function (appErrorPrefix) { 21 | this.config.appErrorPrefix = appErrorPrefix; 22 | }; 23 | 24 | this.$get = function() { 25 | return {config: this.config}; 26 | }; 27 | } 28 | 29 | config.$inject = ['$provide']; 30 | 31 | /** 32 | * Configure by setting an optional string value for appErrorPrefix. 33 | * Accessible via config.appErrorPrefix (via config value). 34 | * @param {Object} $provide 35 | */ 36 | /* @ngInject */ 37 | function config($provide) { 38 | $provide.decorator('$exceptionHandler', extendExceptionHandler); 39 | } 40 | 41 | extendExceptionHandler.$inject = ['$delegate', 'exceptionHandler', 'logger']; 42 | 43 | /** 44 | * Extend the $exceptionHandler service to also display a toast. 45 | * @param {Object} $delegate 46 | * @param {Object} exceptionHandler 47 | * @param {Object} logger 48 | * @return {Function} the decorated $exceptionHandler service 49 | */ 50 | function extendExceptionHandler($delegate, exceptionHandler, logger) { 51 | return function(exception, cause) { 52 | var appErrorPrefix = exceptionHandler.config.appErrorPrefix || ''; 53 | var errorData = {exception: exception, cause: cause}; 54 | exception.message = appErrorPrefix + exception.message; 55 | $delegate(exception, cause); 56 | /** 57 | * Could add the error to a service's collection, 58 | * add errors to $rootScope, log errors to remote web server, 59 | * or log locally. Or throw hard. It is entirely up to you. 60 | * throw exception; 61 | * 62 | * @example 63 | * throw { message: 'error message we added' }; 64 | */ 65 | logger.error(exception.message, errorData); 66 | }; 67 | } 68 | })(); 69 | -------------------------------------------------------------------------------- /src/client/app/blocks/exception/exception-handler.provider.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('blocks.exception', function() { 3 | var exceptionHandlerProvider; 4 | var mocks = { 5 | errorMessage: 'fake error', 6 | prefix: '[TEST]: ' 7 | }; 8 | 9 | beforeEach(function() { 10 | bard.appModule('blocks.exception', function(_exceptionHandlerProvider_) { 11 | exceptionHandlerProvider = _exceptionHandlerProvider_; 12 | }); 13 | bard.inject('$rootScope'); 14 | }); 15 | 16 | bard.verifyNoOutstandingHttpRequests(); 17 | 18 | describe('exceptionHandlerProvider', function() { 19 | it('should have a dummy test', inject(function() { 20 | expect(true).to.equal(true); 21 | })); 22 | 23 | it('should have exceptionHandlerProvider defined', inject(function() { 24 | expect(exceptionHandlerProvider).to.be.defined; 25 | })); 26 | 27 | it('should have configuration', inject(function() { 28 | expect(exceptionHandlerProvider.config).to.be.defined; 29 | })); 30 | 31 | it('should have configuration', inject(function() { 32 | expect(exceptionHandlerProvider.configure).to.be.defined; 33 | })); 34 | 35 | describe('with appErrorPrefix', function() { 36 | beforeEach(function() { 37 | exceptionHandlerProvider.configure(mocks.prefix); 38 | }); 39 | 40 | it('should have appErrorPrefix defined', inject(function() { 41 | expect(exceptionHandlerProvider.$get().config.appErrorPrefix).to.be.defined; 42 | })); 43 | 44 | it('should have appErrorPrefix set properly', inject(function() { 45 | expect(exceptionHandlerProvider.$get().config.appErrorPrefix) 46 | .to.equal(mocks.prefix); 47 | })); 48 | 49 | it('should throw an error when forced', inject(function() { 50 | expect(functionThatWillThrow).to.throw(); 51 | })); 52 | 53 | it('manual error is handled by decorator', function() { 54 | var exception; 55 | exceptionHandlerProvider.configure(mocks.prefix); 56 | try { 57 | $rootScope.$apply(functionThatWillThrow); 58 | } 59 | catch (ex) { 60 | exception = ex; 61 | expect(ex.message).to.equal(mocks.prefix + mocks.errorMessage); 62 | } 63 | }); 64 | }); 65 | }); 66 | 67 | function functionThatWillThrow() { 68 | throw new Error(mocks.errorMessage); 69 | } 70 | }); 71 | -------------------------------------------------------------------------------- /src/client/app/blocks/exception/exception.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('blocks.exception') 6 | .factory('exception', exception); 7 | 8 | /* @ngInject */ 9 | function exception($q, logger) { 10 | var service = { 11 | catcher: catcher 12 | }; 13 | return service; 14 | 15 | function catcher(message) { 16 | return function(e) { 17 | var thrownDescription; 18 | var newMessage; 19 | if (e.data && e.data.description) { 20 | thrownDescription = '\n' + e.data.description; 21 | newMessage = message + thrownDescription; 22 | } 23 | e.data.description = newMessage; 24 | logger.error(newMessage); 25 | return $q.reject(e); 26 | }; 27 | } 28 | } 29 | })(); 30 | -------------------------------------------------------------------------------- /src/client/app/blocks/exception/exception.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('blocks.exception', ['blocks.logger']); 5 | })(); 6 | -------------------------------------------------------------------------------- /src/client/app/blocks/logger/logger.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('blocks.logger') 6 | .factory('logger', logger); 7 | 8 | logger.$inject = ['$log', 'toastr']; 9 | 10 | /* @ngInject */ 11 | function logger($log, toastr) { 12 | var service = { 13 | showToasts: true, 14 | 15 | error : error, 16 | info : info, 17 | success : success, 18 | warning : warning, 19 | 20 | // straight to console; bypass toastr 21 | log : $log.log 22 | }; 23 | 24 | return service; 25 | ///////////////////// 26 | 27 | function error(message, data, title) { 28 | toastr.error(message, title); 29 | $log.error('Error: ' + message, data); 30 | } 31 | 32 | function info(message, data, title) { 33 | toastr.info(message, title); 34 | $log.info('Info: ' + message, data); 35 | } 36 | 37 | function success(message, data, title) { 38 | toastr.success(message, title); 39 | $log.info('Success: ' + message, data); 40 | } 41 | 42 | function warning(message, data, title) { 43 | toastr.warning(message, title); 44 | $log.warn('Warning: ' + message, data); 45 | } 46 | } 47 | }()); 48 | -------------------------------------------------------------------------------- /src/client/app/blocks/logger/logger.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('blocks.logger', []); 5 | })(); 6 | -------------------------------------------------------------------------------- /src/client/app/blocks/router/router-helper.provider.js: -------------------------------------------------------------------------------- 1 | /* Help configure the state-base ui.router */ 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('blocks.router') 7 | .provider('routerHelper', routerHelperProvider); 8 | 9 | routerHelperProvider.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider']; 10 | /* @ngInject */ 11 | function routerHelperProvider($locationProvider, $stateProvider, $urlRouterProvider) { 12 | /* jshint validthis:true */ 13 | var config = { 14 | docTitle: undefined, 15 | resolveAlways: {} 16 | }; 17 | 18 | $locationProvider.html5Mode(true); 19 | 20 | this.configure = function(cfg) { 21 | angular.extend(config, cfg); 22 | }; 23 | 24 | this.$get = RouterHelper; 25 | RouterHelper.$inject = ['$location', '$rootScope', '$state', 'logger']; 26 | /* @ngInject */ 27 | function RouterHelper($location, $rootScope, $state, logger) { 28 | var handlingStateChangeError = false; 29 | var hasOtherwise = false; 30 | var stateCounts = { 31 | errors: 0, 32 | changes: 0 33 | }; 34 | 35 | var service = { 36 | configureStates: configureStates, 37 | getStates: getStates, 38 | stateCounts: stateCounts 39 | }; 40 | 41 | init(); 42 | 43 | return service; 44 | 45 | /////////////// 46 | 47 | function configureStates(states, otherwisePath) { 48 | states.forEach(function(state) { 49 | state.config.resolve = 50 | angular.extend(state.config.resolve || {}, config.resolveAlways); 51 | $stateProvider.state(state.state, state.config); 52 | }); 53 | if (otherwisePath && !hasOtherwise) { 54 | hasOtherwise = true; 55 | $urlRouterProvider.otherwise(otherwisePath); 56 | } 57 | } 58 | 59 | function handleRoutingErrors() { 60 | // Route cancellation: 61 | // On routing error, go to the dashboard. 62 | // Provide an exit clause if it tries to do it twice. 63 | $rootScope.$on('$stateChangeError', 64 | function(event, toState, toParams, fromState, fromParams, error) { 65 | if (handlingStateChangeError) { 66 | return; 67 | } 68 | stateCounts.errors++; 69 | handlingStateChangeError = true; 70 | var destination = (toState && 71 | (toState.title || toState.name || toState.loadedTemplateUrl)) || 72 | 'unknown target'; 73 | var msg = 'Error routing to ' + destination + '. ' + 74 | (error.data || '') + '.
' + (error.statusText || '') + 75 | ': ' + (error.status || ''); 76 | logger.warning(msg, [toState]); 77 | $location.path('/'); 78 | } 79 | ); 80 | } 81 | 82 | function init() { 83 | handleRoutingErrors(); 84 | updateDocTitle(); 85 | } 86 | 87 | function getStates() { return $state.get(); } 88 | 89 | function updateDocTitle() { 90 | $rootScope.$on('$stateChangeSuccess', 91 | function(event, toState, toParams, fromState, fromParams) { 92 | stateCounts.changes++; 93 | handlingStateChangeError = false; 94 | var title = config.docTitle + ' ' + (toState.title || ''); 95 | $rootScope.title = title; // data bind to 96 | } 97 | ); 98 | } 99 | } 100 | } 101 | })(); 102 | -------------------------------------------------------------------------------- /src/client/app/blocks/router/router.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('blocks.router', [ 5 | 'ui.router', 6 | 'blocks.logger' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /src/client/app/clickjacking/clickjacking.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.clickjacking') 6 | .controller('ClickjackingController', ClickjackingController); 7 | 8 | ClickjackingController.$inject = ['$q', 'userservice', 'logger']; 9 | /* @ngInject */ 10 | function ClickjackingController($q, userservice, logger) { 11 | var vm = this; 12 | vm.title = 'Clickjacking'; 13 | vm.deleteProfile = deleteProfile; 14 | 15 | activate(); 16 | 17 | function activate() { 18 | logger.info('Activated Clickjacking View'); 19 | } 20 | 21 | function deleteProfile() { 22 | logger.info('The profile was successfully deleted!'); 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /src/client/app/clickjacking/clickjacking.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('ClickjackingController', function() { 3 | var controller; 4 | var people = mockData.getMockPeople(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.clickjacking'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'dataservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(dataservice, 'getPeople').returns($q.when(people)); 13 | controller = $controller('ClickjackingController'); 14 | $rootScope.$apply(); 15 | }); 16 | 17 | bard.verifyNoOutstandingHttpRequests(); 18 | 19 | describe('Clickjacking controller', function() { 20 | it('should be created successfully', function () { 21 | expect(controller).to.be.defined; 22 | }); 23 | 24 | describe('after activate', function() { 25 | it('should have title of Clickjacking', function () { 26 | expect(controller.title).to.equal('Clickjacking'); 27 | }); 28 | 29 | it('should have logged "Activated"', function() { 30 | expect($log.info.logs).to.match(/Activated/); 31 | }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/client/app/clickjacking/clickjacking.html: -------------------------------------------------------------------------------- 1 | <section id="clickjacking-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-6"> 6 | <div class="widget wblue"> 7 | <div ht-widget-header title="Examples" 8 | allow-collapse="true"></div> 9 | <div class="widget-content text-center text-info"> 10 | <p>See the attacker-app clickjacking-attack view for more details on this vulnerability</p> 11 | <button ng-click="vm.deleteProfile()">Delete Sensitive Information!</button> 12 | </div> 13 | <div class="widget-foot"> 14 | <div class="clearfix"></div> 15 | </div> 16 | </div> 17 | </div> 18 | </div> 19 | </div> 20 | </section> 21 | </section> -------------------------------------------------------------------------------- /src/client/app/clickjacking/clickjacking.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.clickjacking', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /src/client/app/clickjacking/clickjacking.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.clickjacking') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'clickjacking', 18 | config: { 19 | url: '/clickjacking', 20 | templateUrl: 'app/clickjacking/clickjacking.html', 21 | controller: 'ClickjackingController', 22 | controllerAs: 'vm', 23 | title: 'Clickjacking', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-clickjacking"></i> Delete it All!' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /src/client/app/clickjacking/clickjacking.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('clickjacking routes', function () { 3 | describe('state', function () { 4 | var view = 'app/clickjacking/clickjacking.html'; 5 | 6 | beforeEach(function() { 7 | module('app.clickjacking', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state clickjacking to url /clickjacking ', function() { 18 | expect($state.href('clickjacking', {})).to.equal('/clickjacking'); 19 | }); 20 | 21 | it('should map /clickjacking route to clickjacking View template', function () { 22 | expect($state.get('clickjacking').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of clickjacking should work with $state.go', function () { 26 | $state.go('clickjacking'); 27 | $rootScope.$apply(); 28 | expect($state.is('clickjacking')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/client/app/core/404.html: -------------------------------------------------------------------------------- 1 | <section id="dashboard-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-12"> 6 | <ul class="today-datas"> 7 | <li class="bred"> 8 | <div class="pull-left"><i class="fa fa-warning"></i></div> 9 | <div class="datas-text pull-right"> 10 | <a><span class="bold">404</span></a>Page Not Found 11 | </div> 12 | <div class="clearfix"></div> 13 | </li> 14 | </ul> 15 | </div> 16 | </div> 17 | <div class="row"> 18 | <div class="widget wblue"> 19 | <div ht-widget-header title="Page Not Found" 20 | allow-collapse="true"></div> 21 | <div class="widget-content text-center text-info"> 22 | <div class="container"> 23 | No soup for you! 24 | </div> 25 | </div> 26 | <div class="widget-foot"> 27 | <div class="clearfix"></div> 28 | </div> 29 | </div> 30 | </div> 31 | </div> 32 | </section> 33 | </section> 34 | -------------------------------------------------------------------------------- /src/client/app/core/config.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var core = angular.module('app.core'); 5 | 6 | core.config(toastrConfig); 7 | 8 | toastrConfig.$inject = ['toastr']; 9 | /* @ngInject */ 10 | function toastrConfig(toastr) { 11 | toastr.options.timeOut = 4000; 12 | toastr.options.positionClass = 'toast-bottom-right'; 13 | } 14 | 15 | var config = { 16 | appErrorPrefix: '[vulnerable-app Error] ', 17 | appTitle: 'vulnerable-app' 18 | }; 19 | 20 | core.value('config', config); 21 | 22 | core.config(configure); 23 | 24 | configure.$inject = ['$logProvider', 'routerHelperProvider', 'exceptionHandlerProvider']; 25 | /* @ngInject */ 26 | function configure($logProvider, routerHelperProvider, exceptionHandlerProvider) { 27 | if ($logProvider.debugEnabled) { 28 | $logProvider.debugEnabled(true); 29 | } 30 | exceptionHandlerProvider.configure(config.appErrorPrefix); 31 | routerHelperProvider.configure({docTitle: config.appTitle + ': '}); 32 | } 33 | 34 | })(); 35 | -------------------------------------------------------------------------------- /src/client/app/core/constants.js: -------------------------------------------------------------------------------- 1 | /* global toastr:false, moment:false */ 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('app.core') 7 | .constant('toastr', toastr) 8 | .constant('moment', moment); 9 | })(); 10 | -------------------------------------------------------------------------------- /src/client/app/core/core.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.core', [ 6 | 'ngAnimate', 'ngSanitize', 7 | 'blocks.exception', 'blocks.logger', 'blocks.router', 8 | 'ui.router', 'ngplus' 9 | ]); 10 | })(); 11 | -------------------------------------------------------------------------------- /src/client/app/core/core.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.core') 6 | .run(appRun); 7 | 8 | /* @ngInject */ 9 | function appRun(routerHelper) { 10 | var otherwise = '/404'; 11 | routerHelper.configureStates(getStates(), otherwise); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: '404', 18 | config: { 19 | url: '/404', 20 | templateUrl: 'app/core/404.html', 21 | title: '404' 22 | } 23 | } 24 | ]; 25 | } 26 | })(); 27 | -------------------------------------------------------------------------------- /src/client/app/core/core.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('core', function() { 3 | describe('state', function() { 4 | var views = { 5 | four0four: 'app/core/404.html' 6 | }; 7 | 8 | beforeEach(function() { 9 | module('app.core', bard.fakeToastr); 10 | bard.inject('$location', '$rootScope', '$state', '$templateCache'); 11 | $templateCache.put(views.core, ''); 12 | }); 13 | 14 | it('should map /404 route to 404 View template', function() { 15 | expect($state.get('404').templateUrl).to.equal(views.four0four); 16 | }); 17 | 18 | it('of dashboard should work with $state.go', function() { 19 | $state.go('404'); 20 | $rootScope.$apply(); 21 | expect($state.is('404')); 22 | }); 23 | 24 | it('should route /invalid to the otherwise (404) route', function() { 25 | $location.path('/invalid'); 26 | $rootScope.$apply(); 27 | expect($state.current.templateUrl).to.equal(views.four0four); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/client/app/core/dataservice.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.core') 6 | .factory('dataservice', dataservice); 7 | 8 | dataservice.$inject = ['$http', '$q', 'exception', 'logger']; 9 | /* @ngInject */ 10 | function dataservice($http, $q, exception, logger) { 11 | var service = { 12 | getPeople: getPeople, 13 | getMessageCount: getMessageCount, 14 | search: search 15 | }; 16 | 17 | return service; 18 | 19 | function search(searchTerm) { 20 | return $http.get('/api/search?searchTerm=' + searchTerm) 21 | .then(success) 22 | .catch(fail); 23 | 24 | function success(response) { 25 | return response.data; 26 | } 27 | 28 | function fail(e) { 29 | return exception.catcher('XHR Failed for search')(e); 30 | } 31 | } 32 | 33 | function getMessageCount() { return $q.when(72); } 34 | 35 | function getPeople() { 36 | return $http.get('/api/people') 37 | .then(success) 38 | .catch(fail); 39 | 40 | function success(response) { 41 | return response.data; 42 | } 43 | 44 | function fail(e) { 45 | return exception.catcher('XHR Failed for getPeople')(e); 46 | } 47 | } 48 | } 49 | })(); 50 | -------------------------------------------------------------------------------- /src/client/app/core/user.service.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('app.core') 7 | .factory('userservice', userservice); 8 | 9 | userservice.$inject = ['$rootScope', '$http', '$q', 'exception', 'logger']; 10 | /* @ngInject */ 11 | function userservice($rootScope, $http, $q, exception, logger) { 12 | var service = { 13 | login: login, 14 | logout: logout, 15 | updateProfile: updateProfile, 16 | getProfile: getProfile 17 | }; 18 | 19 | return service; 20 | 21 | function getProfile() { 22 | return $http.get('/api/user/profile') 23 | .then(function(response) { 24 | return response.data; 25 | }) 26 | .catch(fail); 27 | } 28 | 29 | function updateProfile(profile) { 30 | return $http.post('/api/user/profile', profile) 31 | .then(function(response) { 32 | return response.data; 33 | }) 34 | .catch(fail); 35 | } 36 | 37 | function logout() { 38 | return $http.post('/api/user/logout') 39 | .then(function(response) { 40 | $rootScope.isLoggedIn = true; 41 | return response.data; 42 | }) 43 | .catch(fail); 44 | } 45 | 46 | function login() { 47 | return $http.post('/api/user/login') 48 | .then(function(response) { 49 | $rootScope.isLoggedIn = false; 50 | return response.data; 51 | }) 52 | .catch(fail); 53 | } 54 | 55 | function fail(e) { 56 | return exception.catcher('XHR Failed')(e); 57 | } 58 | } 59 | })(); 60 | -------------------------------------------------------------------------------- /src/client/app/csrf/csrf.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.csrf') 6 | .controller('CsrfController', CsrfController); 7 | 8 | CsrfController.$inject = ['$q', 'userservice', 'logger', '$sce']; 9 | /* @ngInject */ 10 | function CsrfController($q, userservice, logger, $sce) { 11 | var vm = this; 12 | vm.title = 'CSRF'; 13 | vm.user = { 14 | firstName: 'Test', 15 | lastName: 'Test' 16 | }; 17 | vm.updateProfile = updateProfile; 18 | vm.getProfile = getProfile; 19 | 20 | activate(); 21 | 22 | function activate() { 23 | logger.info('Activated CSRF View'); 24 | getProfile(); 25 | } 26 | 27 | function updateProfile(user) { 28 | console.log('First: ', user.firstName); 29 | console.log('Last: ', user.lastName); 30 | console.log('User Profile: ', JSON.stringify(user)); 31 | userservice.updateProfile(user) 32 | .then(function(response) { 33 | logger.info('Successfully updated the user profile'); 34 | }) 35 | .catch(function(error) { 36 | logger.error('There was an error updating the user profile: ', error); 37 | }); 38 | } 39 | 40 | function getProfile() { 41 | userservice.getProfile() 42 | .then(function(response) { 43 | logger.info('Received profile data: ', response); 44 | vm.user.firstName = response.firstName; 45 | vm.user.lastName = $sce.trustAsHtml(response.lastName); 46 | // vm.zz = $sce.trustAsHtml("<b>hello</b><script>alert(1)</script>"); 47 | // vm.zz = $sce.trustAsHtml(vm.user.lastName); 48 | }) 49 | .catch(function(error) { 50 | logger.error(error); 51 | }); 52 | } 53 | } 54 | })(); 55 | -------------------------------------------------------------------------------- /src/client/app/csrf/csrf.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('CsrfController', function() { 3 | var controller; 4 | var profile = mockData.getMockProfile(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.csrf'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'userservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(userservice, 'getProfile').returns($q.when(profile)); 13 | sinon.stub(userservice, 'updateProfile').returns($q.when(profile)); 14 | controller = $controller('CsrfController'); 15 | $rootScope.$apply(); 16 | }); 17 | 18 | bard.verifyNoOutstandingHttpRequests(); 19 | 20 | describe('Csrf controller', function() { 21 | it('should be created successfully', function () { 22 | expect(controller).to.be.defined; 23 | }); 24 | 25 | describe('after activate', function() { 26 | it('should have title of Csrf', function () { 27 | expect(controller.title).to.equal('CSRF'); 28 | }); 29 | 30 | it('should have logged "Activated"', function() { 31 | expect($log.info.logs).to.match(/Activated/); 32 | }); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/client/app/csrf/csrf.html: -------------------------------------------------------------------------------- 1 | <section id="csrf-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-6"> 6 | <div class="widget wblue"> 7 | <div ht-widget-header title="Update Profile" 8 | allow-collapse="true"></div> 9 | <div class="widget-content text-center text-info"> 10 | <form> 11 | <label>Update Your Profile:</label><br><br> 12 | <span class="pull-left"><label>First Name:<label><input type="text" ng-model="user.firstName"></input></span> 13 | <span class="pull-left"><label>Last Name:<label><input type="text" ng-model="user.lastName"></input></span> 14 | <input type="submit" ng-click="vm.updateProfile(user)" value="Submit"></input><br><br> 15 | </form> 16 | </div> 17 | <div class="widget-foot"> 18 | <div class="clearfix"></div> 19 | </div> 20 | </div> 21 | </div> 22 | <div class="col-md-6"> 23 | <div class="widget wblue"> 24 | <div ht-widget-header title="User Profile" 25 | allow-collapse="true"></div> 26 | <div class="widget-content text-center text-info"> 27 | <button ng-click="vm.getProfile()">Get Latest User Profile</button> 28 | <br> 29 | <br> 30 | <label>First Name: </label><span ng-bind-html="vm.user.firstName"></span> 31 | <br> 32 | <label>Last Name: </label><span ng-bind-html="vm.user.lastName"></span> 33 | <!-- <pre ng-bind-html="vm.zz"></pre> --> 34 | </div> 35 | <div class="widget-foot"> 36 | <div class="clearfix"></div> 37 | </div> 38 | </div> 39 | </div> 40 | </div> 41 | </div> 42 | </section> 43 | </section> -------------------------------------------------------------------------------- /src/client/app/csrf/csrf.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.csrf', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /src/client/app/csrf/csrf.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.csrf') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'csrf', 18 | config: { 19 | url: '/csrf', 20 | templateUrl: 'app/csrf/csrf.html', 21 | controller: 'CsrfController', 22 | controllerAs: 'vm', 23 | title: 'CSRF', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-csrf"></i> Profile' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /src/client/app/csrf/csrf.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('csrf routes', function () { 3 | describe('state', function () { 4 | var view = 'app/csrf/csrf.html'; 5 | 6 | beforeEach(function() { 7 | module('app.csrf', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state csrf to url /csrf ', function() { 18 | expect($state.href('csrf', {})).to.equal('/csrf'); 19 | }); 20 | 21 | it('should map /csrf route to csrf View template', function () { 22 | expect($state.get('csrf').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of csrf should work with $state.go', function () { 26 | $state.go('csrf'); 27 | $rootScope.$apply(); 28 | expect($state.is('csrf')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/client/app/dashboard/dashboard.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.dashboard') 6 | .controller('DashboardController', DashboardController); 7 | 8 | DashboardController.$inject = ['$q', 'dataservice', 'logger']; 9 | /* @ngInject */ 10 | function DashboardController($q, dataservice, logger) { 11 | var vm = this; 12 | vm.news = { 13 | title: 'vulnerable-app', 14 | description: 'Hot Towel Angular is a SPA template for Angular developers.' 15 | }; 16 | vm.messageCount = 0; 17 | vm.people = []; 18 | vm.title = 'Dashboard'; 19 | 20 | activate(); 21 | 22 | function activate() { 23 | var promises = [getMessageCount(), getPeople()]; 24 | return $q.all(promises).then(function() { 25 | logger.info('Activated Dashboard View'); 26 | }); 27 | } 28 | 29 | function getMessageCount() { 30 | return dataservice.getMessageCount().then(function (data) { 31 | vm.messageCount = data; 32 | return vm.messageCount; 33 | }); 34 | } 35 | 36 | function getPeople() { 37 | return dataservice.getPeople().then(function (data) { 38 | vm.people = data; 39 | return vm.people; 40 | }); 41 | } 42 | } 43 | })(); 44 | -------------------------------------------------------------------------------- /src/client/app/dashboard/dashboard.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('DashboardController', function() { 3 | var controller; 4 | var people = mockData.getMockPeople(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.dashboard'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'dataservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(dataservice, 'getPeople').returns($q.when(people)); 13 | controller = $controller('DashboardController'); 14 | $rootScope.$apply(); 15 | }); 16 | 17 | bard.verifyNoOutstandingHttpRequests(); 18 | 19 | describe('Dashboard controller', function() { 20 | it('should be created successfully', function () { 21 | expect(controller).to.be.defined; 22 | }); 23 | 24 | describe('after activate', function() { 25 | it('should have title of Dashboard', function () { 26 | expect(controller.title).to.equal('Dashboard'); 27 | }); 28 | 29 | it('should have logged "Activated"', function() { 30 | expect($log.info.logs).to.match(/Activated/); 31 | }); 32 | 33 | it('should have news', function () { 34 | expect(controller.news).to.not.be.empty; 35 | }); 36 | 37 | it('should have at least 1 person', function () { 38 | expect(controller.people).to.have.length.above(0); 39 | }); 40 | 41 | it('should have people count of 5', function () { 42 | expect(controller.people).to.have.length(7); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/client/app/dashboard/dashboard.html: -------------------------------------------------------------------------------- 1 | <section id="dashboard-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-12"> 6 | <div class="widget wviolet"> 7 | <div ht-widget-header title="Welcome" 8 | allow-collapse="true"></div> 9 | <div class="widget-content text-center text-info"> 10 | <p> 11 | Welcome to this security demo. The purpose of this application is to demonstrate security vulnerabilities through an application that doesn't implement any mitigation techniques. This particular application is used for demonstrating Cross-Site Scripting (XSS), Cross-Site Request Forgery and Clickjacking vulnerabilities. 12 | </p> 13 | </div> 14 | <div class="widget-foot"> 15 | <div class="clearfix"></div> 16 | </div> 17 | </div> 18 | </div> 19 | </div> 20 | </div> 21 | </section> 22 | </section> -------------------------------------------------------------------------------- /src/client/app/dashboard/dashboard.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.dashboard', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /src/client/app/dashboard/dashboard.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.dashboard') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'dashboard', 18 | config: { 19 | url: '/', 20 | templateUrl: 'app/dashboard/dashboard.html', 21 | controller: 'DashboardController', 22 | controllerAs: 'vm', 23 | title: 'dashboard', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-dashboard"></i> Dashboard' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /src/client/app/dashboard/dashboard.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('dashboard routes', function () { 3 | describe('state', function () { 4 | var view = 'app/dashboard/dashboard.html'; 5 | 6 | beforeEach(function() { 7 | module('app.dashboard', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state dashboard to url / ', function() { 18 | expect($state.href('dashboard', {})).to.equal('/'); 19 | }); 20 | 21 | it('should map /dashboard route to dashboard View template', function () { 22 | expect($state.get('dashboard').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of dashboard should work with $state.go', function () { 26 | $state.go('dashboard'); 27 | $rootScope.$apply(); 28 | expect($state.is('dashboard')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/client/app/layout/ht-sidebar.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .directive('htSidebar', htSidebar); 7 | 8 | /* @ngInject */ 9 | function htSidebar () { 10 | // Opens and closes the sidebar menu. 11 | // Usage: 12 | // <div ht-sidebar"> 13 | // <div ht-sidebar whenDoneAnimating="vm.sidebarReady()"> 14 | // Creates: 15 | // <div ht-sidebar class="sidebar"> 16 | var directive = { 17 | link: link, 18 | restrict: 'EA', 19 | scope: { 20 | whenDoneAnimating: '&?' 21 | } 22 | }; 23 | return directive; 24 | 25 | function link(scope, element, attrs) { 26 | var $sidebarInner = element.find('.sidebar-inner'); 27 | var $dropdownElement = element.find('.sidebar-dropdown a'); 28 | element.addClass('sidebar'); 29 | $dropdownElement.click(dropdown); 30 | 31 | function dropdown(e) { 32 | var dropClass = 'dropy'; 33 | e.preventDefault(); 34 | if (!$dropdownElement.hasClass(dropClass)) { 35 | $sidebarInner.slideDown(350, scope.whenDoneAnimating); 36 | $dropdownElement.addClass(dropClass); 37 | } else if ($dropdownElement.hasClass(dropClass)) { 38 | $dropdownElement.removeClass(dropClass); 39 | $sidebarInner.slideUp(350, scope.whenDoneAnimating); 40 | } 41 | } 42 | } 43 | } 44 | })(); 45 | -------------------------------------------------------------------------------- /src/client/app/layout/ht-sidebar.directive.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | /* jshint multistr:true */ 3 | describe('htSidebar directive: ', function () { 4 | var dropdownElement; 5 | var el; 6 | var innerElement; 7 | var isOpenClass = 'dropy'; 8 | var scope; 9 | 10 | beforeEach(module('app.layout')); 11 | 12 | beforeEach(inject(function($compile, $rootScope) { 13 | // The minimum necessary template HTML for this spec. 14 | // Simulates a menu link that opens and closes a dropdown of menu items 15 | // The `when-done-animating` attribute is optional (as is the vm's implementation) 16 | // 17 | // N.B.: the attribute value is supposed to be an expression that invokes a $scope method 18 | // so make sure the expression includes '()', e.g., "vm.sidebarReady(42)" 19 | // no harm if the expression fails ... but then scope.sidebarReady will be undefined. 20 | // All parameters in the expression are passed to vm.sidebarReady ... if it exists 21 | // 22 | // N.B.: We do NOT add this element to the browser DOM (although we could). 23 | // spec runs faster if we don't touch the DOM (even the PhantomJS DOM). 24 | el = angular.element( 25 | '<ht-sidebar when-done-animating="vm.sidebarReady(42)">' + 26 | '<div class="sidebar-dropdown"><a href="">Menu</a></div>' + 27 | '<div class="sidebar-inner" style="display: none"></div>' + 28 | '</ht-sidebar>'); 29 | 30 | // The spec examines changes to these template parts 31 | dropdownElement = el.find('.sidebar-dropdown a'); // the link to click 32 | innerElement = el.find('.sidebar-inner'); // container of menu items 33 | 34 | // ng's $compile service resolves nested directives (there are none in this example) 35 | // and binds the element to the scope (which must be a real ng scope) 36 | scope = $rootScope; 37 | $compile(el)(scope); 38 | 39 | // tell angular to look at the scope values right now 40 | scope.$digest(); 41 | })); 42 | 43 | /// tests /// 44 | describe('the isOpenClass', function () { 45 | it('is absent for a closed menu', function () { 46 | hasIsOpenClass(false); 47 | }); 48 | 49 | it('is added to a closed menu after clicking', function () { 50 | clickIt(); 51 | hasIsOpenClass(true); 52 | }); 53 | 54 | it('is present for an open menu', function () { 55 | openDropdown(); 56 | hasIsOpenClass(true); 57 | }); 58 | 59 | it('is removed from a closed menu after clicking', function () { 60 | openDropdown(); 61 | clickIt(); 62 | hasIsOpenClass(false); 63 | }); 64 | }); 65 | 66 | describe('when animating w/ jQuery fx off', function () { 67 | beforeEach(function () { 68 | // remember current state of jQuery's global FX duration switch 69 | this.oldFxOff = $.fx.off; 70 | // when jQuery fx are of, there is zero animation time; no waiting for animation to complete 71 | $.fx.off = true; 72 | // must add to DOM when testing jQuery animation result 73 | el.appendTo(document.body); 74 | }); 75 | 76 | afterEach(function () { 77 | $.fx.off = this.oldFxOff; 78 | el.remove(); 79 | }); 80 | 81 | it('dropdown is visible after opening a closed menu', function () { 82 | dropdownIsVisible(false); // hidden before click 83 | clickIt(); 84 | dropdownIsVisible(true); // visible after click 85 | }); 86 | 87 | it('dropdown is hidden after closing an open menu', function () { 88 | openDropdown(); 89 | dropdownIsVisible(true); // visible before click 90 | clickIt(); 91 | dropdownIsVisible(false); // hidden after click 92 | }); 93 | 94 | it('click triggers "when-done-animating" expression', function () { 95 | // spy on directive's callback when the animation is done 96 | var spy = sinon.spy(); 97 | 98 | // Recall the pertinent tag in the template ... 99 | // ' <div ht-sidebar when-done-animating="vm.sidebarReady(42)" > 100 | // therefore, the directive looks for scope.vm.sidebarReady 101 | // and should call that method with the value '42' 102 | scope.vm = {sidebarReady: spy}; 103 | 104 | // tell angular to look again for that vm.sidebarReady property 105 | scope.$digest(); 106 | 107 | // spy not called until after click which triggers the animation 108 | expect(spy).not.to.have.been.called; 109 | 110 | // this click triggers an animation 111 | clickIt(); 112 | 113 | // verify that the vm's method (sidebarReady) was called with '42' 114 | // FYI: spy.args[0] is the array of args passed to sidebarReady() 115 | expect(spy).to.have.been.called; 116 | expect(spy).to.have.been.calledWith(42); 117 | }); 118 | }); 119 | 120 | /////// helpers ////// 121 | 122 | // put the dropdown in the 'menu open' state 123 | function openDropdown() { 124 | dropdownElement.addClass(isOpenClass); 125 | innerElement.css('display', 'block'); 126 | } 127 | 128 | // click the "menu" link 129 | function clickIt() { 130 | dropdownElement.trigger('click'); 131 | } 132 | 133 | // assert whether the "menu" link has the class that means 'is open' 134 | function hasIsOpenClass(isTrue) { 135 | var hasClass = dropdownElement.hasClass(isOpenClass); 136 | expect(hasClass).equal(!!isTrue, 137 | 'dropdown has the "is open" class is ' + hasClass); 138 | } 139 | 140 | // assert whether the dropdown container is 'block' (visible) or 'none' (hidden) 141 | function dropdownIsVisible(isTrue) { 142 | var display = innerElement.css('display'); 143 | expect(display).to.equal(isTrue ? 'block' : 'none', 144 | 'innerElement display value is ' + display); 145 | } 146 | }); 147 | -------------------------------------------------------------------------------- /src/client/app/layout/ht-top-nav.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .directive('htTopNav', htTopNav); 7 | 8 | /* @ngInject */ 9 | function htTopNav () { 10 | var directive = { 11 | bindToController: true, 12 | controller: TopNavController, 13 | controllerAs: 'vm', 14 | restrict: 'EA', 15 | scope: { 16 | 'navline': '=' 17 | }, 18 | templateUrl: 'app/layout/ht-top-nav.html' 19 | }; 20 | 21 | /* @ngInject */ 22 | function TopNavController($rootScope, $timeout, userservice) { 23 | var vm = this; 24 | vm.isLoggedIn = $rootScope.isLoggedIn; 25 | vm.user = undefined; 26 | vm.logout = logout; 27 | vm.login = login; 28 | 29 | activate(); 30 | 31 | function activate() { 32 | login(); 33 | } 34 | 35 | function logout() { 36 | $rootScope.showSplash = true; 37 | userservice.logout() 38 | .then(function(response) { 39 | $rootScope.isLoggedIn = false; 40 | vm.isLoggedIn = false; 41 | vm.user = undefined; 42 | console.log($rootScope.isLoggedIn); 43 | console.log(vm.isLoggedIn); 44 | hideSplash(); 45 | }) 46 | .catch(function(error) { 47 | console.log('ERROR: ', error); 48 | }); 49 | } 50 | 51 | function login() { 52 | $rootScope.showSplash = true; 53 | userservice.login() 54 | .then(function(response) { 55 | $rootScope.isLoggedIn = true; 56 | vm.isLoggedIn = true; 57 | sessionStorage.setItem('userAuthToken', response); 58 | console.log('login response: ', response); 59 | vm.user = response; 60 | hideSplash(); 61 | }) 62 | .catch(function(error) { 63 | console.log('ERROR: ', error); 64 | }); 65 | } 66 | 67 | function hideSplash() { 68 | //Force a 1 second delay so we can see the splash. 69 | $timeout(function() { 70 | $rootScope.showSplash = false; 71 | }, 1000); 72 | } 73 | } 74 | 75 | return directive; 76 | } 77 | })(); 78 | -------------------------------------------------------------------------------- /src/client/app/layout/ht-top-nav.html: -------------------------------------------------------------------------------- 1 | <nav class="navbar navbar-fixed-top navbar-inverse"> 2 | <div class="navbar-header"> 3 | <a href="/" class="navbar-brand"><span class="brand-title">{{vm.navline.title}}</span></a> 4 | <a class="btn navbar-btn navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 5 | <span class="icon-bar"></span> 6 | <span class="icon-bar"></span> 7 | <span class="icon-bar"></span> 8 | </a> 9 | </div> 10 | <div class="navbar-collapse collapse"> 11 | <div class="pull-left navbar-header navbar-brand"> 12 | <div ng-show="vm.isLoggedIn"> 13 | <span class="user-title">Hello<!-- , Brian Clark: {{vm.user}} --></span> 14 | <button class="btn btn-primary btn-xs" ng-click="vm.logout()">Logout</button> 15 | </div> 16 | <div ng-show="!vm.isLoggedIn"> 17 | <button class="btn btn-primary btn-xs" ng-click="vm.login()">Login</button> 18 | </div> 19 | </div> 20 | </div> 21 | </nav> 22 | -------------------------------------------------------------------------------- /src/client/app/layout/layout.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.layout', ['app.core']); 5 | })(); 6 | -------------------------------------------------------------------------------- /src/client/app/layout/shell.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .controller('ShellController', ShellController); 7 | 8 | ShellController.$inject = ['$rootScope', '$timeout', 'config', 'logger']; 9 | /* @ngInject */ 10 | function ShellController($rootScope, $timeout, config, logger) { 11 | var vm = this; 12 | vm.busyMessage = 'Please wait ...'; 13 | vm.isBusy = true; 14 | $rootScope.showSplash = true; 15 | vm.navline = { 16 | title: config.appTitle, 17 | text: 'Created by John Papa', 18 | link: 'http://twitter.com/john_papa' 19 | }; 20 | 21 | activate(); 22 | 23 | function activate() { 24 | logger.success(config.appTitle + ' loaded!', null); 25 | hideSplash(); 26 | } 27 | 28 | function hideSplash() { 29 | //Force a 1 second delay so we can see the splash. 30 | $timeout(function() { 31 | $rootScope.showSplash = false; 32 | }, 1000); 33 | } 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /src/client/app/layout/shell.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('ShellController', function() { 3 | var controller; 4 | 5 | beforeEach(function() { 6 | bard.appModule('app.layout'); 7 | bard.inject('$controller', '$q', '$rootScope', '$timeout', 'dataservice'); 8 | }); 9 | 10 | beforeEach(function () { 11 | controller = $controller('ShellController'); 12 | $rootScope.$apply(); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | describe('Shell controller', function() { 18 | it('should be created successfully', function () { 19 | expect(controller).to.be.defined; 20 | }); 21 | 22 | it('should show splash screen', function () { 23 | expect($rootScope.showSplash).to.be.true; 24 | }); 25 | 26 | it('should hide splash screen after timeout', function (done) { 27 | $timeout(function() { 28 | expect($rootScope.showSplash).to.be.false; 29 | done(); 30 | }, 1000); 31 | $timeout.flush(); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/client/app/layout/shell.html: -------------------------------------------------------------------------------- 1 | <div ng-controller="ShellController as vm"> 2 | <header class="clearfix"> 3 | <ht-top-nav navline="vm.navline"></ht-top-nav> 4 | </header> 5 | <section id="content" class="content"> 6 | <div ng-include="'app/layout/sidebar.html'"></div> 7 | 8 | <div ui-view class="shuffle-animation"></div> 9 | 10 | <div ngplus-overlay 11 | ngplus-overlay-delay-in="50" 12 | ngplus-overlay-delay-out="700" 13 | ngplus-overlay-animation="dissolve-animation"> 14 | <img src="images/busy.gif"/> 15 | 16 | <div class="page-spinner-message overlay-message">{{vm.busyMessage}}</div> 17 | </div> 18 | </section> 19 | </div> 20 | -------------------------------------------------------------------------------- /src/client/app/layout/sidebar.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.layout') 6 | .controller('SidebarController', SidebarController); 7 | 8 | SidebarController.$inject = ['$state', 'routerHelper']; 9 | /* @ngInject */ 10 | function SidebarController($state, routerHelper) { 11 | var vm = this; 12 | var states = routerHelper.getStates(); 13 | vm.isCurrent = isCurrent; 14 | 15 | activate(); 16 | 17 | function activate() { getNavRoutes(); } 18 | 19 | function getNavRoutes() { 20 | vm.navRoutes = states.filter(function(r) { 21 | return r.settings && r.settings.nav; 22 | }).sort(function(r1, r2) { 23 | return r1.settings.nav - r2.settings.nav; 24 | }); 25 | } 26 | 27 | function isCurrent(route) { 28 | if (!route.title || !$state.current || !$state.current.title) { 29 | return ''; 30 | } 31 | var menuName = route.title; 32 | return $state.current.title.substr(0, menuName.length) === menuName ? 'current' : ''; 33 | } 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /src/client/app/layout/sidebar.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('layout', function() { 3 | describe('sidebar', function() { 4 | var controller; 5 | var views = { 6 | dashboard: 'app/dashboard/dashboard.html', 7 | customers: 'app/customers/customers.html' 8 | }; 9 | 10 | beforeEach(function() { 11 | module('app.layout', bard.fakeToastr); 12 | bard.inject('$controller', '$httpBackend', '$location', 13 | '$rootScope', '$state', 'routerHelper'); 14 | }); 15 | 16 | beforeEach(function() { 17 | routerHelper.configureStates(mockData.getMockStates(), '/'); 18 | controller = $controller('SidebarController'); 19 | $rootScope.$apply(); 20 | }); 21 | 22 | bard.verifyNoOutstandingHttpRequests(); 23 | 24 | it('should have isCurrent() for / to return `current`', function() { 25 | $location.path('/'); 26 | expect(controller.isCurrent($state.current)).to.equal('current'); 27 | }); 28 | 29 | it('should have isCurrent() for /customers to return `current`', function() { 30 | $location.path('/customers'); 31 | expect(controller.isCurrent($state.current)).to.equal('current'); 32 | }); 33 | 34 | it('should have isCurrent() for non route not return `current`', function() { 35 | $location.path('/invalid'); 36 | expect(controller.isCurrent({title: 'invalid'})).not.to.equal('current'); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/client/app/layout/sidebar.html: -------------------------------------------------------------------------------- 1 | <div ng-controller="SidebarController as vm"> 2 | <ht-sidebar when-done-animating="vm.sidebarReady()"> 3 | <div class="sidebar-filler"></div> 4 | <div class="sidebar-dropdown"><a href="#">Menu</a></div> 5 | <div class="sidebar-inner"> 6 | <div class="sidebar-widget"></div> 7 | <ul class="navi"> 8 | <li class="nlightblue fade-selection-animation" ng-class="vm.isCurrent(r)" 9 | ng-repeat="r in vm.navRoutes"> 10 | <a ui-sref="{{r.name}}" 11 | ng-bind-html="r.settings.content"></a> 12 | </li> 13 | </ul> 14 | </div> 15 | </ht-sidebar> 16 | </div> 17 | -------------------------------------------------------------------------------- /src/client/app/widgets/ht-img-person.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.widgets') 6 | .directive('htImgPerson', htImgPerson); 7 | 8 | htImgPerson.$inject = ['config']; 9 | /* @ngInject */ 10 | function htImgPerson (config) { 11 | //Usage: 12 | //<img ht-img-person="{{person.imageSource}}"/> 13 | var basePath = config.imageBasePath; 14 | var unknownImage = config.unknownPersonImageSource; 15 | var directive = { 16 | link: link, 17 | restrict: 'A' 18 | }; 19 | return directive; 20 | 21 | function link(scope, element, attrs) { 22 | attrs.$observe('htImgPerson', function (value) { 23 | value = basePath + (value || unknownImage); 24 | attrs.$set('src', value); 25 | }); 26 | } 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /src/client/app/widgets/ht-widget-header.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.widgets') 6 | .directive('htWidgetHeader', htWidgetHeader); 7 | 8 | /* @ngInject */ 9 | function htWidgetHeader() { 10 | //Usage: 11 | //<div ht-widget-header title="vm.map.title"></div> 12 | // Creates: 13 | // <div ht-widget-header="" 14 | // title="Movie" 15 | // allow-collapse="true" </div> 16 | var directive = { 17 | scope: { 18 | 'title': '@', 19 | 'subtitle': '@', 20 | 'rightText': '@', 21 | 'allowCollapse': '@' 22 | }, 23 | templateUrl: 'app/widgets/widget-header.html', 24 | restrict: 'EA' 25 | }; 26 | return directive; 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /src/client/app/widgets/widget-header.html: -------------------------------------------------------------------------------- 1 | <div class="widget-head"> 2 | <div class="page-title pull-left">{{title}}</div> 3 | <small class="page-title-subtle" ng-show="subtitle">({{subtitle}})</small> 4 | <div class="widget-icons pull-right"></div> 5 | <small class="pull-right page-title-subtle" ng-show="rightText">{{rightText}}</small> 6 | <div class="clearfix"></div> 7 | </div> -------------------------------------------------------------------------------- /src/client/app/widgets/widgets.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.widgets', []); 5 | })(); 6 | -------------------------------------------------------------------------------- /src/client/app/xss-search/xss-search.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.xss-search') 6 | .controller('XssSearchController', XssSearchController); 7 | 8 | XssSearchController.$inject = ['$q', '$sanitize', '$sce', 'dataservice', 'logger', '$location', 9 | '$scope', '$timeout']; 10 | /* @ngInject */ 11 | function XssSearchController($q, $sanitize, $sce, dataservice, logger, $location, 12 | $scope, $timeout) { 13 | var vm = this; 14 | vm.title = 'XSS Search'; 15 | vm.search = search; 16 | vm.searchResults = undefined; 17 | 18 | $timeout(function () { 19 | $scope.searchTerm = $location.hash(); 20 | vm.search($scope.searchTerm); 21 | }, 1000); 22 | 23 | activate(); 24 | 25 | function activate() { 26 | logger.info('Activated XSS Search View'); 27 | } 28 | 29 | function search(searchTerm) { 30 | dataservice.search(searchTerm) 31 | .then(function(response) { 32 | // Intentionally trusting as HTML for demonstration purposes 33 | console.log('Search response: ', response); 34 | $location.hash(response); 35 | vm.searchResults = $sce.trustAsHtml(response); 36 | }) 37 | .catch(function(error) { 38 | logger.error(error); 39 | }); 40 | } 41 | } 42 | })(); 43 | -------------------------------------------------------------------------------- /src/client/app/xss-search/xss-search.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('XssSearchController', function() { 3 | var controller; 4 | var people = mockData.getMockPeople(); 5 | 6 | beforeEach(function() { 7 | bard.appModule('app.xss-search'); 8 | bard.inject('$controller', '$log', '$q', '$rootScope', 'dataservice'); 9 | }); 10 | 11 | beforeEach(function () { 12 | sinon.stub(dataservice, 'getPeople').returns($q.when(people)); 13 | controller = $controller('XssSearchController'); 14 | $rootScope.$apply(); 15 | }); 16 | 17 | bard.verifyNoOutstandingHttpRequests(); 18 | 19 | describe('XssSearch controller', function() { 20 | it('should be created successfully', function () { 21 | expect(controller).to.be.defined; 22 | }); 23 | 24 | describe('after activate', function() { 25 | it('should have title of XssSearch', function () { 26 | expect(controller.title).to.equal('XSS Search'); 27 | }); 28 | 29 | it('should have logged "Activated"', function() { 30 | expect($log.info.logs).to.match(/Activated/); 31 | }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/client/app/xss-search/xss-search.html: -------------------------------------------------------------------------------- 1 | <section id="xss-search-view" class="mainbar"> 2 | <section class="matter"> 3 | <div class="container"> 4 | <div class="row"> 5 | <div class="col-md-12"> 6 | <div class="widget wgreen"> 7 | <div ht-widget-header title="Search" 8 | allow-collapse="true"></div> 9 | <div class="widget-content text-center text-info"> 10 | <form> 11 | <label>Search:</label> 12 | <input type="text" ng-model="searchTerm" class="search"></input> 13 | <input type="submit" ng-click="vm.search(searchTerm)" value="Submit"></input> 14 | </form> 15 | <hr> 16 | <label>Result:</label> 17 | <div> 18 | <pre ng-bind-html="vm.searchResults"> 19 | </pre> 20 | </div> 21 | </div> 22 | <div class="widget-foot"> 23 | <div class="clearfix"></div> 24 | </div> 25 | </div> 26 | </div> 27 | </div> 28 | </div> 29 | </section> 30 | </section> -------------------------------------------------------------------------------- /src/client/app/xss-search/xss-search.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('app.xss-search', [ 5 | 'app.core', 6 | 'app.widgets' 7 | ]); 8 | })(); 9 | -------------------------------------------------------------------------------- /src/client/app/xss-search/xss-search.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app.xss-search') 6 | .run(appRun); 7 | 8 | appRun.$inject = ['routerHelper']; 9 | /* @ngInject */ 10 | function appRun(routerHelper) { 11 | routerHelper.configureStates(getStates()); 12 | } 13 | 14 | function getStates() { 15 | return [ 16 | { 17 | state: 'xss-search', 18 | config: { 19 | url: '/xss-search', 20 | templateUrl: 'app/xss-search/xss-search.html', 21 | controller: 'XssSearchController', 22 | controllerAs: 'vm', 23 | title: 'XSS Search', 24 | settings: { 25 | nav: 1, 26 | content: '<i class="fa fa-xss-search"></i> Search' 27 | } 28 | } 29 | } 30 | ]; 31 | } 32 | })(); 33 | -------------------------------------------------------------------------------- /src/client/app/xss-search/xss-search.route.spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W117, -W030 */ 2 | describe('xss-search routes', function () { 3 | describe('state', function () { 4 | var view = 'app/xss-search/xss-search.html'; 5 | 6 | beforeEach(function() { 7 | module('app.xss-search', bard.fakeToastr); 8 | bard.inject('$httpBackend', '$location', '$rootScope', '$state', '$templateCache'); 9 | }); 10 | 11 | beforeEach(function() { 12 | $templateCache.put(view, ''); 13 | }); 14 | 15 | bard.verifyNoOutstandingHttpRequests(); 16 | 17 | it('should map state xss-search to url /xss-search ', function() { 18 | expect($state.href('xss-search', {})).to.equal('/xss-search'); 19 | }); 20 | 21 | it('should map /xss-search route to xss-search View template', function () { 22 | expect($state.get('xss-search').templateUrl).to.equal(view); 23 | }); 24 | 25 | it('of xss-search should work with $state.go', function () { 26 | $state.go('xss-search'); 27 | $rootScope.$apply(); 28 | expect($state.is('xss-search')); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/client/images/AngularJS-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/src/client/images/AngularJS-small.png -------------------------------------------------------------------------------- /src/client/images/busy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/src/client/images/busy.gif -------------------------------------------------------------------------------- /src/client/images/gulp-tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/src/client/images/gulp-tiny.png -------------------------------------------------------------------------------- /src/client/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/src/client/images/icon.png -------------------------------------------------------------------------------- /src/client/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html ng-app="app"> 3 | <head> 4 | <style> 5 | /* This helps the ng-show/ng-hide animations start at the right place. */ 6 | /* Since Angular has this but needs to load, this gives us the class early. */ 7 | .ng-hide { display: none!important; } 8 | </style> 9 | <title ng-bind="title">vulnerable-app 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 | vulnerable-app 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/client/specs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spec Runner 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

Spec Runner

24 |

Make sure the REMOTE server is running
25 | Click on a description title to narrow the scope to just its specs 26 | (see " 27 | ?grep" in address bar).
28 | Click on a spec title to see the test implementation.
29 | Click on page title to start over. 30 |

31 | 32 |
33 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/client/test-helpers/bind-polyfill.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Phantom.js does not support Function.prototype.bind (at least not before v.2.0 3 | * That's just crazy. Everybody supports bind. 4 | * Read about it here: https://groups.google.com/forum/#!msg/phantomjs/r0hPOmnCUpc/uxusqsl2LNoJ 5 | * This polyfill is copied directly from MDN 6 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility 7 | */ 8 | if (!Function.prototype.bind) { 9 | /*jshint freeze: false */ 10 | Function.prototype.bind = function (oThis) { 11 | if (typeof this !== 'function') { 12 | // closest thing possible to the ECMAScript 5 13 | // internal IsCallable function 14 | var msg = 'Function.prototype.bind - what is trying to be bound is not callable'; 15 | throw new TypeError(msg); 16 | } 17 | 18 | var aArgs = Array.prototype.slice.call(arguments, 1), 19 | fToBind = this, 20 | FuncNoOp = function () {}, 21 | fBound = function () { 22 | return fToBind.apply(this instanceof FuncNoOp && oThis ? this : oThis, 23 | aArgs.concat(Array.prototype.slice.call(arguments))); 24 | }; 25 | 26 | FuncNoOp.prototype = this.prototype; 27 | fBound.prototype = new FuncNoOp(); 28 | 29 | return fBound; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/client/test-helpers/mock-data.js: -------------------------------------------------------------------------------- 1 | /* jshint -W079 */ 2 | var mockData = (function() { 3 | return { 4 | getMockPeople: getMockPeople, 5 | getMockStates: getMockStates, 6 | getMockProfile: getMockProfile 7 | }; 8 | 9 | function getMockStates() { 10 | return [ 11 | { 12 | state: 'dashboard', 13 | config: { 14 | url: '/', 15 | templateUrl: 'app/dashboard/dashboard.html', 16 | title: 'dashboard', 17 | settings: { 18 | nav: 1, 19 | content: ' Dashboard' 20 | } 21 | } 22 | } 23 | ]; 24 | } 25 | 26 | function getMockPeople() { 27 | return [ 28 | {firstName: 'John', lastName: 'Papa', age: 25, location: 'Florida'}, 29 | {firstName: 'Ward', lastName: 'Bell', age: 31, location: 'California'}, 30 | {firstName: 'Colleen', lastName: 'Jones', age: 21, location: 'New York'}, 31 | {firstName: 'Madelyn', lastName: 'Green', age: 18, location: 'North Dakota'}, 32 | {firstName: 'Ella', lastName: 'Jobs', age: 18, location: 'South Dakota'}, 33 | {firstName: 'Landon', lastName: 'Gates', age: 11, location: 'South Carolina'}, 34 | {firstName: 'Haley', lastName: 'Guthrie', age: 35, location: 'Wyoming'} 35 | ]; 36 | } 37 | 38 | function getMockProfile() { 39 | return { 40 | firstName: 'Test', 41 | lastName: 'Test' 42 | }; 43 | } 44 | })(); 45 | -------------------------------------------------------------------------------- /src/server/app.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | 'use strict'; 3 | 4 | var express = require('express'); 5 | var app = express(); 6 | // app.disable('x-powered-by'); 7 | var bodyParser = require('body-parser'); 8 | var cookieParser = require('cookie-parser'); 9 | var favicon = require('serve-favicon'); 10 | var logger = require('morgan'); 11 | var port = process.env.PORT || 8001; 12 | var four0four = require('./utils/404')(); 13 | 14 | var environment = process.env.NODE_ENV; 15 | 16 | app.use(favicon(__dirname + '/favicon.ico')); 17 | app.use(bodyParser.urlencoded({extended: true})); 18 | app.use(bodyParser.json()); 19 | app.use(cookieParser()); 20 | app.use(logger('dev')); 21 | 22 | // app.use(function(req, res, next) { 23 | // res.setHeader('Content-Security-Policy', 'script-src \'self\' ajax.googleapis.com'); 24 | // return next(); 25 | // }); 26 | 27 | app.use('/api', require('./routes')); 28 | 29 | console.log('About to crank up node'); 30 | console.log('PORT=' + port); 31 | console.log('NODE_ENV=' + environment); 32 | 33 | switch (environment){ 34 | case 'build': 35 | console.log('** BUILD **'); 36 | app.use(express.static('./build/')); 37 | // Any invalid calls for templateUrls are under app/* and should return 404 38 | app.use('/app/*', function(req, res, next) { 39 | four0four.send404(req, res); 40 | }); 41 | // Any deep link calls should return index.html 42 | app.use('/*', express.static('./build/index.html')); 43 | break; 44 | default: 45 | console.log('** DEV **'); 46 | app.use(express.static('./src/client/')); 47 | app.use(express.static('./')); 48 | app.use(express.static('./tmp')); 49 | // Any invalid calls for templateUrls are under app/* and should return 404 50 | app.use('/app/*', function(req, res, next) { 51 | four0four.send404(req, res); 52 | }); 53 | // Any deep link calls should return index.html 54 | app.use('/*', express.static('./src/client/index.html')); 55 | break; 56 | } 57 | 58 | app.listen(port, function() { 59 | console.log('Express server listening on port ' + port); 60 | console.log('env = ' + app.get('env') + 61 | '\n__dirname = ' + __dirname + 62 | '\nprocess.cwd = ' + process.cwd()); 63 | }); 64 | -------------------------------------------------------------------------------- /src/server/data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | people: getPeople() 3 | }; 4 | 5 | function getPeople() { 6 | return [ 7 | {id: 1, firstName: 'John', lastName: 'Papa', age: 25, location: 'Florida'}, 8 | {id: 2, firstName: 'Ward', lastName: 'Bell', age: 31, location: 'California'}, 9 | {id: 3, firstName: 'Colleen', lastName: 'Jones', age: 21, location: 'New York'}, 10 | {id: 4, firstName: 'Madelyn', lastName: 'Green', age: 18, location: 'North Dakota'}, 11 | {id: 5, firstName: 'Ella', lastName: 'Jobs', age: 18, location: 'South Dakota'}, 12 | {id: 6, firstName: 'Landon', lastName: 'Gates', age: 11, location: 'South Carolina'}, 13 | {id: 7, firstName: 'Haley', lastName: 'Guthrie', age: 35, location: 'Wyoming'}, 14 | {id: 8, firstName: 'Aaron', lastName: 'Jinglehiemer', age: 22, location: 'Utah'} 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /src/server/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/vulnerable-app/2d76b72c40c4953982e29f894bdde2e5173dd405/src/server/favicon.ico -------------------------------------------------------------------------------- /src/server/routes.js: -------------------------------------------------------------------------------- 1 | var router = require('express').Router(); 2 | var four0four = require('./utils/404')(); 3 | var data = require('./data'); 4 | data.profile = {}; 5 | 6 | router.get('/people', getPeople); 7 | router.get('/person/:id', getPerson); 8 | 9 | router.post('/user/login', login); 10 | router.post('/user/logout', logout); 11 | 12 | router.post('/user/profile/', updateProfile); 13 | router.get('/user/profile/', getProfile); 14 | 15 | router.get('/search', search); 16 | 17 | router.get('/*', four0four.notFoundMiddleware); 18 | 19 | module.exports = router; 20 | 21 | ////////////// 22 | 23 | function search(req, res, next) { 24 | // This would then query against a datastore for the search term 25 | // and return the results with the search term used for the query 26 | 27 | // For demo purposes we're just going to send back the search term received 28 | console.log(req.query.searchTerm); 29 | res.status(200).send(req.query.searchTerm); 30 | } 31 | 32 | function getProfile(req, res, next) { 33 | console.log('User Requesting Read: ', req.cookies.userAuthToken); 34 | console.log('Profile found: ', data.profile[req.cookies.userAuthToken]); 35 | res.status(200).send(data.profile[req.cookies.userAuthToken]); 36 | } 37 | 38 | function updateProfile(req, res, next) { 39 | var user = req.cookies.userAuthToken; 40 | console.log('User Requesting Update: ', user); 41 | console.log('Updating user profile from: ', data.profile[user]); 42 | console.log('Updating user profile to: ', req.body); 43 | console.log('User Found: ', user); 44 | 45 | data.profile[user] = req.body; 46 | console.log('Updated profile for user: ', user); 47 | 48 | res.status(200).send(data.profile[user]); 49 | } 50 | 51 | function login(req, res, next) { 52 | var randomNumber = Math.random().toString(); 53 | randomNumber = '35592211433686316';//randomNumber.substring(2, randomNumber.length); 54 | 55 | data.randomNumber = randomNumber; 56 | data.profile[randomNumber] = { 57 | firstName: 'Jim', 58 | lastName: 'Bob' 59 | }; 60 | 61 | console.log('Logged in user: ', data.randomNumber); 62 | res.cookie('userAuthToken', randomNumber, {maxAge: 3600000, path: '/'}); 63 | res.status(200).send(randomNumber); 64 | } 65 | 66 | function logout(req, res, next) { 67 | console.log('Logged out user: ', data.randomNumber); 68 | data.randomNumber = undefined; 69 | res.clearCookie('userAuthToken'); 70 | res.status(200).send('logged out!'); 71 | } 72 | 73 | function getPeople(req, res, next) { 74 | res.status(200).send(data.people); 75 | } 76 | 77 | function getPerson(req, res, next) { 78 | var id = +req.params.id; 79 | var person = data.people.filter(function(p) { 80 | return p.id === id; 81 | })[0]; 82 | 83 | if (person) { 84 | res.status(200).send(person); 85 | } else { 86 | four0four.send404(req, res, 'person ' + id + ' not found'); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/server/utils/404.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | var service = { 3 | notFoundMiddleware: notFoundMiddleware, 4 | send404: send404 5 | }; 6 | return service; 7 | 8 | function notFoundMiddleware(req, res, next) { 9 | send404(req, res, 'API endpoint not found'); 10 | } 11 | 12 | function send404(req, res, description) { 13 | var data = { 14 | status: 404, 15 | message: 'Not Found', 16 | description: description, 17 | url: req.url 18 | }; 19 | res.status(404) 20 | .send(data) 21 | .end(); 22 | } 23 | }; 24 | --------------------------------------------------------------------------------