├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin └── console ├── lib ├── generators │ ├── vrt.rb │ └── vrt │ │ └── install_generator.rb ├── vrt.rb └── vrt │ ├── cross_version_mapping.rb │ ├── errors.rb │ ├── map.rb │ ├── mapping.rb │ ├── node.rb │ ├── third_party_links.rb │ └── version.rb ├── spec ├── sample_vrt │ ├── 1.0 │ │ └── vulnerability-rating-taxonomy.json │ ├── 2.0 │ │ ├── deprecated-node-mapping.json │ │ ├── mappings │ │ │ ├── cvss_v3.json │ │ │ ├── cwe.json │ │ │ └── remediation_advice.json │ │ ├── third-party-mappings │ │ │ └── remediation_training │ │ │ │ └── secure-code-warrior-links.json │ │ └── vulnerability-rating-taxonomy.json │ └── 999.999 │ │ ├── deprecated-node-mapping.json │ │ ├── mappings │ │ ├── cvss_v3 │ │ │ └── cvss_v3.json │ │ ├── cwe.json │ │ ├── remediation_advice.json │ │ └── test_mapping.json │ │ ├── third-party-mappings │ │ └── secure-code-warrior-links.json │ │ └── vulnerability-rating-taxonomy.json ├── spec_helper.rb ├── vrt │ ├── map_spec.rb │ ├── mappings_spec.rb │ ├── node_spec.rb │ └── third_party_links_spec.rb └── vrt_spec.rb └── vrt.gemspec /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 4 | 5 | # Checklist: 6 | 7 | - [ ] I have made corresponding changes to the documentation 8 | - [ ] I have added tests that prove my fix is effective or that my feature works 9 | - [ ] I have added entries to `CHANGELOG.md` and marked it Added/Changed/Removed 10 | - [ ] I have not incremented `version.rb` 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | ruby: ['3.1','3.2','3.3'] 11 | name: Test ruby v${{ matrix.ruby }} support 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Set up Ruby ${{ matrix.ruby }} 15 | uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: ${{ matrix.ruby }} 18 | - name: Build with dependencies 19 | run: | 20 | gem install bundler -v 2.5.14 21 | bundle install 22 | - name: Test with rspec 23 | run: | 24 | bundle exec rspec spec/ 25 | - name: Lint with rubocop 26 | run: | 27 | bundle exec rubocop 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /doc/ 3 | /pkg/ 4 | /spec/reports/ 5 | /tmp/ 6 | .DS_Store 7 | 8 | # Gemfile.lock should not be under version control 9 | # https://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ 10 | Gemfile.lock 11 | 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/data/1.0"] 2 | path = lib/data/1.0 3 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 4 | [submodule "lib/data/1.1"] 5 | path = lib/data/1.1 6 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 7 | [submodule "lib/data/1.2"] 8 | path = lib/data/1.2 9 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 10 | [submodule "lib/data/1.3"] 11 | path = lib/data/1.3 12 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 13 | [submodule "lib/data/1.3.1"] 14 | path = lib/data/1.3.1 15 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 16 | [submodule "lib/data/1.4"] 17 | path = lib/data/1.4 18 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 19 | [submodule "lib/data/1.5"] 20 | path = lib/data/1.5 21 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 22 | [submodule "lib/data/1.6"] 23 | path = lib/data/1.6 24 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 25 | [submodule "lib/data/1.7"] 26 | path = lib/data/1.7 27 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 28 | [submodule "lib/data/1.7.1"] 29 | path = lib/data/1.7.1 30 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 31 | [submodule "lib/data/1.8"] 32 | path = lib/data/1.8 33 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 34 | [submodule "lib/data/1.9"] 35 | path = lib/data/1.9 36 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 37 | [submodule "lib/data/1.10"] 38 | path = lib/data/1.10 39 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 40 | [submodule "lib/data/1.10.1"] 41 | path = lib/data/1.10.1 42 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 43 | [submodule "lib/data/1.11"] 44 | path = lib/data/1.11 45 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 46 | [submodule "lib/data/1.12"] 47 | path = lib/data/1.12 48 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 49 | [submodule "lib/data/1.13"] 50 | path = lib/data/1.13 51 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 52 | [submodule "lib/data/1.14"] 53 | path = lib/data/1.14 54 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 55 | [submodule "lib/data/1.14.1"] 56 | path = lib/data/1.14.1 57 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 58 | [submodule "lib/data/1.14.2"] 59 | path = lib/data/1.14.2 60 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 61 | [submodule "lib/data/1.15"] 62 | path = lib/data/1.15 63 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 64 | [submodule "lib/data/1.15.1"] 65 | path = lib/data/1.15.1 66 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 67 | [submodule "lib/data/1.16"] 68 | path = lib/data/1.16 69 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 70 | [submodule "lib/data/1.17"] 71 | path = lib/data/1.17 72 | url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git 73 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 3.1 3 | 4 | Style/Documentation: 5 | Enabled: false 6 | 7 | Style/FrozenStringLiteralComment: 8 | Enabled: false 9 | 10 | Metrics/LineLength: 11 | Max: 120 12 | IgnoreCopDirectives: true 13 | 14 | Metrics/BlockLength: 15 | ExcludedMethods: 16 | - configure 17 | - context 18 | - define 19 | - describe 20 | - draw 21 | - factory 22 | - feature 23 | - guard 24 | - included 25 | - it 26 | - let 27 | - let! 28 | - scenario 29 | - setup 30 | - shared_context 31 | - shared_examples 32 | - shared_examples_for 33 | - transaction 34 | 35 | inherit_from: .rubocop_todo.yml 36 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2017-04-18 13:12:12 -0700 using RuboCop version 0.48.1. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 2 10 | Metrics/AbcSize: 11 | Max: 29 12 | 13 | # Offense count: 3 14 | # Configuration parameters: CountComments. 15 | Metrics/MethodLength: 16 | Max: 19 17 | 18 | # Offense count: 1 19 | # Configuration parameters: CountComments. 20 | Metrics/ModuleLength: 21 | Max: 111 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/) 6 | 7 | ## [Unreleased] 8 | 9 | ### Added 10 | 11 | - Find the [Secure Code Warrior](https://www.securecodewarrior.com/) remediation training associated with a VRT node 12 | 13 | ### Changed 14 | 15 | ### Removed 16 | 17 | ## [v0.13.6](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.5...v0.13.6) - 2025-08-19 18 | 19 | ### Added 20 | 21 | - Support for VRT 1.17 22 | 23 | ## [v0.13.5](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.4...v0.13.5) - 2025-06-23 24 | 25 | ### Added 26 | 27 | - Support for VRT 1.16 28 | 29 | ## [v0.13.4](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.3...v0.13.4) - 2025-03-11 30 | 31 | ### Added 32 | 33 | - Support for VRT 1.15.1 34 | 35 | ## [v0.13.3](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.2...v0.13.3) - 2025-02-12 36 | 37 | ### Added 38 | 39 | - Support for VRT 1.15 40 | 41 | ## [v0.13.2](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.1...v0.13.2) - 2024-10-25 42 | 43 | ### Added 44 | 45 | - Support for VRT 1.14.2 46 | 47 | ## [v0.13.1](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.0...v0.13.1) - 2024-07-18 48 | 49 | ### Changed 50 | 51 | - Minor Bugfixes 52 | 53 | ## [v0.13.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.7...v0.13.0) - 2024-07-18 54 | 55 | ### Added 56 | 57 | - Support for VRT 1.14.1 58 | 59 | ### Removed 60 | 61 | - Support for ruby version < 3.1 62 | 63 | ## [v0.12.7](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.6...v0.12.7) - 2024-07-09 64 | 65 | ### Added 66 | 67 | - Support for VRT 1.14 68 | 69 | ## [v0.12.6](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.5...v0.12.6) - 2024-04-02 70 | 71 | ### Added 72 | 73 | - Support for VRT 1.13 74 | 75 | ## [v0.12.5](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.4...v0.12.5) - 2023-12-18 76 | 77 | ### Added 78 | 79 | - Support for VRT 1.12 and some fixes 80 | 81 | ## [v0.12.4](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.3...v0.12.4) - 2023-12-18 82 | 83 | ### Added 84 | 85 | - Support for VRT 1.12 and some fixes 86 | 87 | ## [v0.12.3](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.2...v0.12.3) - 2023-12-18 88 | 89 | ### Added 90 | 91 | - Support for VRT 1.12 92 | 93 | ## [v0.12.2](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.1...v0.12.2) - 2023-11-20 94 | 95 | ### Added 96 | 97 | - Support for VRT 1.11 and some fixes 98 | 99 | ## [v0.12.1](https://github.com/bugcrowd/vrt-ruby/compare/v0.12.0...v0.12.1) - 2023-11-20 100 | 101 | ### Added 102 | 103 | - Support for VRT 1.11 and some minor fixes 104 | 105 | ## [v0.12.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.11.0...v0.12.0) - 2023-11-16 106 | 107 | ### Added 108 | 109 | - Support for VRT 1.11 110 | 111 | ## [v0.11.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.10.0...v0.11.0) - 2021-03-31 112 | 113 | ### Added 114 | 115 | - Support for VRT 1.10 116 | - Support for VRT 1.10.1 (updated spelling in scw mapping) 117 | 118 | ## [v0.10.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.9.0...v0.10.0) - 2020-07-09 119 | 120 | ### Added 121 | 122 | - Support for VRT 1.9 123 | 124 | ## [v0.9.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.8.1...v0.9.0) - 2019-10-04 125 | 126 | ### Added 127 | 128 | - Support for VRT 1.8 129 | 130 | ## [v0.8.1](https://github.com/bugcrowd/vrt-ruby/compare/v0.8.0...v0.8.1) - 2019-04-25 131 | 132 | ### Added 133 | 134 | - Support for VRT 1.7.1 (includes automotive mappings) 135 | 136 | ## [v0.8.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.7.0...v0.8.0) - 2019-03-15 137 | 138 | ### Added 139 | 140 | - Support for nested mappings: 141 | - Support for VRT v1.7 142 | 143 | ### Changed 144 | 145 | - Upgrade ruby version to 2.5.3 146 | 147 | ## [v0.7.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.6.0...v0.7.0) - 2018-11-05 148 | 149 | ### Added 150 | 151 | - Support for VRT v1.6 152 | 153 | ## [v0.6.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.5.1...v0.6.0) - 2018-09-27 154 | 155 | ### Changed 156 | 157 | - Fixed bug for mappings with multiple keys and a default (resolves: [#26](https://github.com/bugcrowd/vrt-ruby/issues/26)) 158 | 159 | ### Removed 160 | 161 | - Removed `Gemfile.lock` from source control 162 | 163 | ## [v0.5.1](https://github.com/bugcrowd/vrt-ruby/compare/v0.5.0...v0.5.1) - 2018-05-15 164 | 165 | ### Changed 166 | 167 | - Mappings with array values will no longer coalesce the mapping default. 168 | The mapping default will only be used in the case where no value is mapped. 169 | 170 | ## [v0.5.0](https://github.com/bugcrowd/vrt-ruby/compare/v0.4.6...v0.5.0) - 2018-05-01 171 | 172 | ### Added 173 | 174 | - VRT 1.4 data 175 | - Support for mappings with `keys` metadata 176 | - CWE mapping 177 | - Bugcrowd Remediation Advice mapping 178 | 179 | ### Changed 180 | 181 | - Mappings with array values now coalesce downwards. 182 | Child VRT nodes will include values from parent nodes if a mapping 183 | provides node data as an array. 184 | 185 | ## [v0.4.6](https://github.com/bugcrowd/vrt-ruby/compare/v0.4.5...v0.4.6) - 2018-02-05 186 | 187 | ### Changed 188 | 189 | - Cache VRT::Map objects (#18) 190 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Bugcrowd welcomes community feedback and direct contributions to the Bugcrowd VRT Ruby Wrapper. We accept comments for public discussion via GitHub Issues. 3 | 4 | ## Process 5 | Please open your feedback as an **Issue** and label it as either a `bug` or an `enhancement`. Large or systemic changes should first be discussed in an Issue rather than be submitted as a pull request directly. 6 | 7 | Prior to opening a pull request please ensure your suggested changes pass specs. The repository uses [`rspec`](https://github.com/rspec/rspec) for spec running, run it with `bundle install && bundle exec rspec`. 8 | 9 | ### Updating the VRT version 10 | When a new version of the VRT is released, we follow these steps: 11 | 1. Add new submodule of the new version tag 12 | - `git submodule add git@github.com:bugcrowd/vulnerability-rating-taxonomy.git lib/data/X.X` 13 | - `cd lib/data/X.X` 14 | - `git checkout vX.X` 15 | 2. Release a new version of the gem (see below) 16 | 3. Update dependent applications 17 | - `bundle update vrt` 18 | 19 | ### Releasing new versions of the gem 20 | 1. Merge all PRs targeted for inclusion in the release (without touching `version.rb`) 21 | 2. Bump the version in `version.rb` 22 | 3. Update CHANGELOG with new version 23 | 4. Commit the version bump `git commit -m [tag name]` (where `tag name` is something like `v0.8.0`) 24 | 5. Tag the commit `git tag [tag name]` (where `tag name` is something like `v0.8.0`) 25 | 6. Push the tag and the commit `git push origin master --tag` 26 | 7. Run `rake release` 27 | 28 | 29 | If you need access to push the gem, create an account on rubygems (if you don't have one already) and then ask one of the existing owners to run `gem owner vrt --add ` 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.1 2 | 3 | RUN mkdir -p /usr/src/app 4 | WORKDIR /usr/src/app 5 | 6 | ADD . /usr/src/app 7 | RUN bundle install 8 | 9 | # TODO add cops 10 | CMD bundle exec rspec && bundle exec rubocop 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in vrt.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Bugcrowd, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

15 | 16 | # VRT Ruby Wrapper 17 | While the Content and Structure is defined in the [Vulnerability Rating Taxonomy Repository](https://github.com/bugcrowd/vulnerability-rating-taxonomy), this defines methods to allow for easy handling of VRT logic. This gem is used and maintained by [Bugcrowd Engineering](https://bugcrowd.com). 18 | 19 | ## Getting Started 20 | Add this line to your application's Gemfile: 21 | ```ruby 22 | gem 'vrt' 23 | ``` 24 | 25 | To create the initializer: 26 | ```bash 27 | rails generate vrt:install 28 | ``` 29 | 30 | ## Usage 31 | 32 | For convenience in development, we provide a utility for spinning up a 33 | playground for playing with the gem. You can invoke it with: 34 | 35 | ```bash 36 | bin/console 37 | ``` 38 | 39 | When one has a VRT Classification ID, one can check it's validity: 40 | ```ruby 41 | vrt = VRT::Map.new 42 | 43 | vrt.valid?('server_side_injection') 44 | => true 45 | 46 | vrt.valid?('test_vrt_classification') 47 | => false 48 | ``` 49 | 50 | Get a pretty output for its lineage: 51 | ```ruby 52 | vrt = VRT::Map.new 53 | 54 | vrt.get_lineage('server_side_injection.file_inclusion.local') 55 | => "Server-Side Injection > File Inclusion > Local" 56 | ``` 57 | 58 | The information within that node: 59 | ```ruby 60 | vrt = VRT::Map.new 61 | 62 | vrt.find_node('server_side_injection.file_inclusion.local') 63 | ``` 64 | Which returns the corresponding [`VRT::Node`](https://github.com/bugcrowd/vrt-ruby/blob/master/lib/vrt/node.rb). This node has a variety of methods: 65 | ```ruby 66 | vrt_map = VRT::Map.new 67 | 68 | node = vrt_map.find_node('server_side_injection.file_inclusion.local') 69 | 70 | node.children # Returns Child Nodes 71 | 72 | node.parent # Returns Parent Node 73 | 74 | node.priority 75 | 76 | node.id 77 | 78 | node.name 79 | 80 | node.mappings # The node's mappings to other classifications 81 | ``` 82 | 83 | ### If you need to deal with translating between versions 84 | VRT module also has a `find_node` method that is version agnostic. This is used to find the best 85 | match for a node under any version and has options to specify a preferred version. 86 | 87 | #### Examples: 88 | 89 | ```ruby 90 | # Find a node in a given preferred version that best maps to the given id 91 | VRT.find_node( 92 | vrt_id: 'social_engineering', 93 | preferred_version: '1.1' 94 | ) 95 | # returns 'other' 96 | 97 | # Aggregate vulnerabilities by category 98 | VRT.find_node( 99 | vrt_id: vrt_id, 100 | max_depth: 'category' 101 | ) 102 | 103 | # Query for vulnerabilities by category while maintaining deprecated mappings by adding 104 | # deprecated ids to the search with `all_matching_categories` 105 | categories_to_search_for += VRT.all_matching_categories(categories_to_search_for) 106 | ``` 107 | 108 | ### Mappings and external links 109 | 110 | #### Mappings 111 | 112 | A mapping is a relationship defined from a node to another classification like cvss or cwe or to 113 | more information like remediation advice. The relationships that are defined in mappings are 114 | maintained by the Bugcrowd team as well as external contributors to the 115 | [VRT repo](https://github.com/bugcrowd/vulnerability-rating-taxonomy/tree/master/mappings). 116 | 117 | ##### Example getting the CWE for a particular VRT ID 118 | 119 | ```ruby 120 | VRT.find_node( 121 | vrt_id: 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' 122 | ).mappings[:cwe] 123 | 124 | => ["CWE-942", "CWE-16"] 125 | ``` 126 | 127 | #### Third party links 128 | 129 | These are simillar to mappings, but the relationships are maintained by an external party instead of 130 | Bugcrowd. 131 | 132 | ##### Example getting Secure Code Warrior training link for a particular VRT ID 133 | 134 | ```ruby 135 | VRT.find_node( 136 | vrt_id: 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' 137 | ).third_party_links[:scw] 138 | 139 | => "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:unsafe_cross_origin_resource_sharing&redirect=true" 140 | ``` -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task default: :spec 7 | 8 | desc 'Override our build task to ensure VRT git submodules are present' 9 | task 'build' do 10 | submodule_status = `git submodule init && git submodule update` 11 | 12 | raise 'git submodules were not up-to-date. Please rebuild!' unless submodule_status.empty? 13 | end 14 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'bundler/setup' 4 | require 'vrt' 5 | require 'pry' 6 | 7 | # An interactive console for the VRT gem 8 | 9 | Pry.start 10 | -------------------------------------------------------------------------------- /lib/generators/vrt.rb: -------------------------------------------------------------------------------- 1 | # Pre-warm the in-memory store of these class instance vars when we launch the 2 | # server. Prevents unnecessary file I/O per-request. 3 | VRT.reload! 4 | -------------------------------------------------------------------------------- /lib/generators/vrt/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/base' 2 | 3 | module Vrt 4 | module Generators 5 | class InstallGenerator < Rails::Generators::Base 6 | source_root(File.expand_path(File.dirname(__dir__))) 7 | def create_initializer_file 8 | copy_file '../vrt.rb', 'config/initializers/vrt.rb' 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/vrt.rb: -------------------------------------------------------------------------------- 1 | # The 'guts' of the VRT library. Probably in need of refactoring in future. 2 | # Will do file I/O the first time it's accessed, and will thereafter hold 3 | # VRT versions in-memory. 4 | 5 | require 'vrt/map' 6 | require 'vrt/node' 7 | require 'vrt/mapping' 8 | require 'vrt/cross_version_mapping' 9 | require 'vrt/errors' 10 | require 'vrt/third_party_links' 11 | 12 | require 'date' 13 | require 'json' 14 | require 'pathname' 15 | 16 | module VRT 17 | DIR = Pathname.new(__dir__).join('data') 18 | OTHER_OPTION = { 'id' => 'other', 19 | 'name' => 'Other', 20 | 'priority' => nil, 21 | 'type' => 'category' }.freeze 22 | MAPPINGS = %i[cvss_v3 remediation_advice cwe].freeze 23 | 24 | @version_json = {} 25 | @last_update = {} 26 | @maps = {} 27 | 28 | module_function 29 | 30 | extend CrossVersionMapping 31 | 32 | # Infer the available versions of the VRT from the names of the files 33 | # in the repo. 34 | # The returned list is in semver order with the current version first. 35 | def versions 36 | @versions ||= json_dir_names.sort_by { |v| Gem::Version.new(v) }.reverse! 37 | end 38 | 39 | # Get the most recent version of the VRT. 40 | def current_version 41 | versions.first 42 | end 43 | 44 | def current_version?(version) 45 | version == current_version 46 | end 47 | 48 | # Get the last updated timestamp of the VRT data (not schema!) 49 | # Passing nil for version will return the latest version. 50 | def last_updated(version = nil) 51 | version ||= current_version 52 | return @last_update[version] if @last_update[version] 53 | 54 | metadata = JSON.parse(json_pathname(version).read)['metadata'] 55 | @last_update[version] = Date.parse(metadata['release_date']) 56 | end 57 | 58 | def current_categories 59 | get_map.categories 60 | end 61 | 62 | # Get all deprecated ids that would match in the given categories from the current version 63 | def all_matching_categories(categories) 64 | cross_version_category_mapping 65 | .select { |key, _value| categories.include?(key) } 66 | .values 67 | .flatten 68 | .uniq 69 | end 70 | 71 | # Finds the best match valid node. First looks at valid nodes in the given new version or finds 72 | # the appropriate deprecated mapping. If neither is found it will walk up the tree to find a 73 | # valid parent node before giving up and returning nil. 74 | # 75 | # @param [String] vrt_id A valid vrt_id 76 | # @param [string] preferred_version (Optional) The preferred vrt_version of the returned node 77 | # (defaults to current_version) 78 | # @param [String] max_depth (Optional) The maximum depth to match in 79 | # @param [String] version (deprecated) This parameter is no longer used 80 | # @return [VRT::Node|Nil] A valid VRT::Node object or nil if no best match could be found 81 | def find_node(vrt_id:, preferred_version: nil, max_depth: 'variant', version: nil) # rubocop:disable Lint/UnusedMethodArgument 82 | new_version = preferred_version || current_version 83 | if get_map(version: new_version).valid?(vrt_id) 84 | get_map(version: new_version).find_node(vrt_id, max_depth:) 85 | elsif deprecated_node?(vrt_id) 86 | find_deprecated_node(vrt_id, preferred_version, max_depth) 87 | else 88 | find_valid_parent_node(vrt_id, new_version, max_depth) 89 | end 90 | end 91 | 92 | # Load the VRT from text files, and parse it as JSON. 93 | # If other: true, we append the OTHER_OPTION hash at runtime (not cached) 94 | def get_json(version: nil, other: true) 95 | version ||= current_version 96 | @version_json[version] ||= json_for_version(version) 97 | other ? @version_json[version] + [OTHER_OPTION] : @version_json[version] 98 | end 99 | 100 | def get_map(version: nil) 101 | version ||= current_version 102 | @maps[version] ||= Map.new(version) 103 | end 104 | 105 | # Get names of directories matching lib/data/-/ 106 | def json_dir_names 107 | DIR.entries 108 | .map(&:basename) 109 | .map(&:to_s) 110 | .select { |dirname| dirname =~ /^[0-9]+\.[0-9]/ }.sort 111 | end 112 | 113 | # Get the Pathname for a particular version 114 | def json_pathname(version) 115 | DIR.join(version, 'vulnerability-rating-taxonomy.json') 116 | end 117 | 118 | # Load and parse JSON for some VRT version 119 | def json_for_version(version) 120 | JSON.parse(json_pathname(version).read)['content'] 121 | end 122 | 123 | def mappings 124 | @mappings ||= Hash[MAPPINGS.map { |name| [name, VRT::Mapping.new(name)] }] 125 | end 126 | 127 | def third_party_links 128 | @third_party_links ||= { 129 | scw: VRT::ThirdPartyLinks.new('secure-code-warrior-links', 'remediation_training') 130 | } 131 | end 132 | 133 | # Cache the VRT contents in-memory, so we're not hitting File I/O multiple times per 134 | # request that needs it. 135 | def reload! 136 | unload! 137 | versions 138 | get_json 139 | get_map 140 | last_updated 141 | third_party_links 142 | mappings 143 | end 144 | 145 | # We separate unload! out, as we need to call it in test environments. 146 | def unload! 147 | @versions = nil 148 | @version_json = {} 149 | @last_update = {} 150 | @maps = {} 151 | @mappings = nil 152 | end 153 | end 154 | -------------------------------------------------------------------------------- /lib/vrt/cross_version_mapping.rb: -------------------------------------------------------------------------------- 1 | module VRT 2 | module CrossVersionMapping 3 | # Maps new_category_id: deprecated_node_id 4 | # and new_subcategory_id: deprecated_node_id 5 | def cross_version_category_mapping 6 | category_map = {} 7 | deprecated_node_json.each do |key, value| 8 | latest_version = value.keys.max_by { |n| Gem::Version.new(n) } 9 | id_list = value[latest_version].split('.') 10 | cat_id = id_list[0] 11 | sub_id = id_list[0..1].join('.') 12 | category_map[cat_id] ? category_map[cat_id] << key : category_map[cat_id] = [key] 13 | category_map[sub_id] ? category_map[sub_id] << key : category_map[sub_id] = [key] 14 | end 15 | category_map 16 | end 17 | 18 | # Map shape: { deprecated_id: { version: new_mapped_id } } 19 | def deprecated_node_json 20 | filename = VRT::DIR.join(current_version, 'deprecated-node-mapping.json') 21 | File.file?(filename) ? JSON.parse(File.read(filename)) : {} 22 | end 23 | 24 | def deprecated_node?(vrt_id) 25 | deprecated_node_json[vrt_id] 26 | end 27 | 28 | def latest_version_for_deprecated_node(vrt_id) 29 | deprecated_node_json[vrt_id].keys.max_by { |n| Gem::Version.new(n) } 30 | end 31 | 32 | def find_deprecated_node(vrt_id, new_version = nil, max_depth = 'variant') 33 | version = latest_version_for_deprecated_node(vrt_id) 34 | node_id = deprecated_node_json[vrt_id][new_version] || deprecated_node_json[vrt_id][version] 35 | new_node = VRT::Map.new(new_version).find_node(node_id, max_depth:) 36 | new_node.nil? ? find_deprecated_node(node_id, new_version, max_depth) : new_node 37 | end 38 | 39 | def find_valid_parent_node(vrt_id, new_version, max_depth) 40 | new_map = VRT::Map.new(new_version) 41 | if new_map.valid?(vrt_id) 42 | new_map.find_node(vrt_id, max_depth:) 43 | else 44 | parent = vrt_id.split('.')[0..-2].join('.') 45 | return nil if parent.empty? 46 | 47 | find_valid_parent_node(parent, new_version, max_depth) 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/vrt/errors.rb: -------------------------------------------------------------------------------- 1 | module VRT 2 | module Errors 3 | class MappingNotFound < StandardError; end 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/vrt/map.rb: -------------------------------------------------------------------------------- 1 | module VRT 2 | class Map 3 | DEPTH_MAP = { 4 | 'category' => 1, 5 | 'subcategory' => 2, 6 | 'variant' => 3 7 | }.freeze 8 | 9 | attr_reader :structure, :version 10 | 11 | def initialize(version = nil) 12 | @version = version || VRT.current_version 13 | @structure = build_structure 14 | @_found_nodes = {} 15 | @_lineages = {} 16 | @_valid_vrt_ids = {} 17 | @_valid_identifiers = {} 18 | end 19 | 20 | def find_node(string, max_depth: 'variant') 21 | @_found_nodes[string + max_depth] ||= walk_node_tree(string, max_depth:) 22 | end 23 | 24 | def valid?(vrt_id) 25 | @_valid_vrt_ids[vrt_id] ||= valid_identifier?(vrt_id) && !find_node(vrt_id).nil? 26 | end 27 | 28 | def get_lineage(string, max_depth: 'variant') 29 | @_lineages[string + max_depth] ||= construct_lineage(string, max_depth) 30 | end 31 | 32 | # Returns list of top level categories in the shape: 33 | # [{ value: category_id, label: category_name }] 34 | def categories 35 | structure.keys.map do |key| 36 | node = find_node(key.to_s, max_depth: 'category') 37 | { value: node.id, label: node.name } 38 | end 39 | end 40 | 41 | private 42 | 43 | def valid_identifier?(vrt_id) 44 | # The upstream json schema in the VRT has changed so we need to support both: 45 | # Current: At least one string of lowercase letters or _, plus up to 2 more with stops (no digits) 46 | # and Old: At least one string of lowercase letters, numbers, or _, 47 | # plus up to 2 more with stops and no leading numbers 48 | @_valid_identifiers[vrt_id] ||= vrt_id =~ /other|\A[a-z][a-z_\d]*(\.[a-z][a-z_\d]*){0,2}\z/ 49 | end 50 | 51 | def construct_lineage(string, max_depth) 52 | return unless valid_identifier?(string) 53 | 54 | lineage = '' 55 | walk_node_tree(string, max_depth:) do |ids, node, level| 56 | return unless node 57 | 58 | lineage += node.name 59 | lineage += ' > ' unless level == ids.length 60 | end 61 | lineage 62 | end 63 | 64 | def walk_node_tree(string, max_depth: 'variant') 65 | id_tokens = string.split('.').map(&:to_sym) 66 | ids = id_tokens.take(DEPTH_MAP[max_depth]) 67 | node = @structure[ids[0]] 68 | ids.each_index do |idx| 69 | level = idx + 1 70 | yield(ids, node, level) if block_given? 71 | node = search(ids, node, level) 72 | end 73 | node 74 | end 75 | 76 | def search(ids, node, level) 77 | last_level = level.eql?(ids.length) 78 | last_level ? node : node&.children&.dig(ids[level]) 79 | end 80 | 81 | def build_structure 82 | VRT.get_json(version: @version).reduce({}, &method(:build_node)) 83 | end 84 | 85 | def build_node(memo, vrt, parent = nil) 86 | node = Node.new(vrt.merge('version' => @version, 'parent' => parent)) 87 | node.children = vrt['children'].reduce({}) { |m, v| build_node(m, v, node) } if node.children? 88 | memo[node.id] = node 89 | memo 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/vrt/mapping.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module VRT 4 | class Mapping 5 | PARENT_DIR = 'mappings' 6 | 7 | def initialize(scheme, subdirectory = nil) 8 | @scheme = scheme.to_s 9 | @parent_directory = File.join(self.class::PARENT_DIR, (subdirectory || @scheme)) 10 | load_mappings 11 | end 12 | 13 | # returns the most specific value provided in the mapping file for the given vrt id 14 | # 15 | # if no mapping file exists for the given version, the mapping file for the earliest version available will be used 16 | # rubocop:disable Metrics/CyclomaticComplexity 17 | # rubocop:disable Metrics/PerceivedComplexity 18 | def get(id_list, version) 19 | # update the vrt id to the first version we have a mapping file for 20 | unless @mappings.key?(version) 21 | id_list = VRT.find_node(vrt_id: id_list.join('.'), preferred_version: @min_version).id_list 22 | version = @min_version 23 | end 24 | mapping = @mappings.dig(version, 'content') || @mappings[version] 25 | default = @mappings.dig(version, 'metadata', 'default') 26 | keys = @mappings.dig(version, 'metadata', 'keys') 27 | if keys 28 | # Convert mappings with multiple keys to be nested under a single 29 | # top-level key. Remediation advice has keys 'remediation_advice' 30 | # and 'references' so we convert it to look like 31 | # { remediation_advice: { remediation_advice: '...', references: [...] } } 32 | keys.each_with_object({}) do |key, acc| 33 | acc[key.to_sym] = get_key( 34 | id_list:, 35 | mapping:, 36 | key: 37 | ) || default&.dig(key) 38 | end 39 | else 40 | get_key(id_list:, mapping:, key: @scheme) || default 41 | end 42 | end 43 | # rubocop:enable Metrics/CyclomaticComplexity 44 | # rubocop:enable Metrics/PerceivedComplexity 45 | 46 | private 47 | 48 | def load_mappings 49 | @mappings = {} 50 | VRT.versions.each do |version| 51 | filename = mapping_file_path(version) 52 | next unless File.file?(filename) 53 | 54 | mapping = JSON.parse(File.read(filename)) 55 | mapping['content'] = key_by_id(mapping['content']) 56 | @mappings[version] = mapping 57 | # VRT.versions is sorted in reverse semver order 58 | # so this will end up as the earliest version with a mapping file 59 | @min_version = version 60 | end 61 | raise VRT::Errors::MappingNotFound if @mappings.empty? 62 | end 63 | 64 | def mapping_file_path(version) 65 | # Supports legacy flat file structure `mappings/cvss.json` 66 | filename = VRT::DIR.join(version, self.class::PARENT_DIR, "#{@scheme}.json") 67 | return filename if File.file?(filename) 68 | 69 | # Supports mappings that are nested under their scheme name e.g. `mappings/cvss/cvss.json` 70 | VRT::DIR.join(version, @parent_directory, "#{@scheme}.json") 71 | end 72 | 73 | # Converts arrays to hashes keyed by the id attribute (as a symbol) for easier lookup. So 74 | # [{'id': 'one', 'foo': 'bar'}, {'id': 'two', 'foo': 'baz'}] 75 | # becomes 76 | # {one: {'id': 'one', 'foo': 'bar'}, two: {'id': 'two', 'foo': 'baz'}} 77 | def key_by_id(mapping) 78 | if mapping.is_a?(Array) && mapping.first.is_a?(Hash) && mapping.first.key?('id') 79 | mapping.each_with_object({}) { |entry, acc| acc[entry['id'].to_sym] = key_by_id(entry) } 80 | elsif mapping.is_a?(Hash) 81 | mapping.transform_values { |value| key_by_id(value) } 82 | else 83 | mapping 84 | end 85 | end 86 | 87 | def get_key(id_list:, mapping:, key:) 88 | # iterate through the id components, keeping track of where we are in the mapping file 89 | # and the most specific mapped value found so far 90 | best_guess = nil 91 | id_list.each do |id| 92 | entry = mapping[id] 93 | break unless entry # mapping file doesn't go this deep, return previous value 94 | 95 | best_guess = merge_arrays(best_guess, entry[key]) if entry[key] 96 | # use the children mapping for the next iteration 97 | mapping = entry['children'] || {} 98 | end 99 | best_guess 100 | end 101 | 102 | def merge_arrays(previous_value, new_value) 103 | if previous_value.is_a?(Array) && new_value.is_a?(Array) 104 | new_value | previous_value 105 | else 106 | new_value 107 | end 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/vrt/node.rb: -------------------------------------------------------------------------------- 1 | module VRT 2 | class Node 3 | attr_reader :id, :name, :priority, :type, :version, :parent, :qualified_vrt_id 4 | attr_accessor :children 5 | 6 | def initialize(attributes = {}) 7 | @id = attributes['id'].to_sym 8 | @name = attributes['name'] 9 | @priority = attributes['priority'] 10 | @type = attributes['type'] 11 | @has_children = attributes.key?('children') 12 | @children = {} 13 | @version = attributes['version'] 14 | @parent = attributes['parent'] 15 | @qualified_vrt_id = construct_vrt_id 16 | end 17 | 18 | def children? 19 | @has_children 20 | end 21 | 22 | def construct_vrt_id 23 | id_list.join('.') 24 | end 25 | 26 | def mappings 27 | VRT.mappings.transform_values { |map| map.get(id_list, @version) } 28 | end 29 | 30 | def third_party_links 31 | VRT.third_party_links.transform_values { |map| map.get(id_list, @version) } 32 | end 33 | 34 | def id_list 35 | parent ? parent.id_list << id : [id] 36 | end 37 | 38 | # Since this object contains references to parent and children, 39 | # as_json must be overridden to avoid unending recursion. 40 | def as_json(options = nil) 41 | json = {} 42 | instance_variables.each do |attribute| 43 | attr_name = attribute.to_s.tr('@', '') 44 | json[attr_name] = case attr_name 45 | when 'parent' 46 | parent&.qualified_vrt_id 47 | when 'children' 48 | children.inject({}) do |c, (k, v)| 49 | c[k] = v.nil? ? v : v.as_json(options) 50 | end 51 | else 52 | instance_variable_get(attribute) 53 | end 54 | end 55 | json 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/vrt/third_party_links.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module VRT 4 | class ThirdPartyLinks < Mapping 5 | PARENT_DIR = 'third-party-mappings' 6 | 7 | # Example: 8 | # scw = VRT::ThirdPartyLinks.new('secure-code-warrior-links', 'remediation_training') 9 | # scw.get(['automotive_security_misconfiguration', 'can', 'injection_dos'], '1.10.1') 10 | 11 | private 12 | 13 | def load_mappings 14 | @mappings = {} 15 | VRT.versions.each do |version| 16 | filename = mapping_file_path(version) 17 | next unless File.file?(filename) 18 | 19 | mapping = JSON.parse(File.read(filename)) 20 | @mappings[version] = mapping 21 | # VRT.versions is sorted in reverse semver order 22 | # so this will end up as the earliest version with a mapping file 23 | @min_version = version 24 | end 25 | raise VRT::Errors::MappingNotFound if @mappings.empty? 26 | end 27 | 28 | # For flat third party links ther is no hierarchical step up 29 | def get_key(id_list:, mapping:, key: nil) # rubocop:disable Lint/UnusedMethodArgument 30 | mapping[id_list.join('.')] 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/vrt/version.rb: -------------------------------------------------------------------------------- 1 | module Vrt 2 | VERSION = '0.13.6'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /spec/sample_vrt/1.0/vulnerability-rating-taxonomy.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "release_date": "2017-02-17T00:00:00+00:00" 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "name": "Server Security Misconfiguration", 9 | "type": "category", 10 | "children": [ 11 | { 12 | "id": "unsafe_cross_origin_resource_sharing", 13 | "name": "Unsafe Cross-Origin Resource Sharing", 14 | "type": "subcategory", 15 | "priority": null 16 | }, 17 | { 18 | "id": "same_site_scripting", 19 | "name": "Same-Site Scripting", 20 | "type": "subcategory", 21 | "priority": 5 22 | }, 23 | { 24 | "id": "ssl_attack_breach_poodle_etc", 25 | "name": "SSL Attack (BREACH, POODLE etc.)", 26 | "type": "subcategory", 27 | "priority": null 28 | }, 29 | { 30 | "id": "using_default_credentials", 31 | "name": "Using Default Credentials", 32 | "type": "subcategory", 33 | "children": [ 34 | { 35 | "id": "production_server", 36 | "name": "Production Server", 37 | "type": "variant", 38 | "priority": 1 39 | }, 40 | { 41 | "id": "staging_development_server", 42 | "name": "Staging/Development Server", 43 | "type": "variant", 44 | "priority": 2 45 | } 46 | ] 47 | }, 48 | { 49 | "id": "misconfigured_dns", 50 | "name": "Misconfigured DNS", 51 | "type": "subcategory", 52 | "children": [ 53 | { 54 | "id": "subdomain_takeover", 55 | "name": "Subdomain Takeover", 56 | "type": "variant", 57 | "priority": 2 58 | } 59 | ] 60 | }, 61 | { 62 | "id": "mail_server_misconfiguration", 63 | "name": "Mail Server Misconfiguration", 64 | "type": "subcategory", 65 | "children": [ 66 | { 67 | "id": "missing_spf_on_email_domain", 68 | "name": "Missing SPF on Email Domain", 69 | "type": "variant", 70 | "priority": 3 71 | }, 72 | { 73 | "id": "email_spoofable_via_third_party_api_misconfiguration", 74 | "name": "Email Spoofable Via Third-Party API Misconfiguration", 75 | "type": "variant", 76 | "priority": 3 77 | }, 78 | { 79 | "id": "missing_spf_on_non_email_domain", 80 | "name": "Missing SPF on Non-Email Domain", 81 | "type": "variant", 82 | "priority": 5 83 | }, 84 | { 85 | "id": "spf_uses_a_soft_fail", 86 | "name": "SPF Uses a Soft Fail", 87 | "type": "variant", 88 | "priority": 5 89 | }, 90 | { 91 | "id": "spf_includes_10_lookups", 92 | "name": "SPF Includes > 10 Lookups", 93 | "type": "variant", 94 | "priority": 5 95 | }, 96 | { 97 | "id": "missing_dmarc", 98 | "name": "Missing DMARC", 99 | "type": "variant", 100 | "priority": 5 101 | } 102 | ] 103 | }, 104 | { 105 | "id": "lack_of_password_confirmation", 106 | "name": "Lack of Password Confirmation", 107 | "type": "subcategory", 108 | "children": [ 109 | { 110 | "id": "change_email_address", 111 | "name": "Change Email Address", 112 | "type": "variant", 113 | "priority": 4 114 | }, 115 | { 116 | "id": "change_password", 117 | "name": "Change Password", 118 | "type": "variant", 119 | "priority": 4 120 | }, 121 | { 122 | "id": "delete_account", 123 | "name": "Delete Account", 124 | "type": "variant", 125 | "priority": 4 126 | } 127 | ] 128 | }, 129 | { 130 | "id": "no_rate_limiting_on_form", 131 | "name": "No Rate Limiting on Form", 132 | "type": "subcategory", 133 | "children": [ 134 | { 135 | "id": "registration", 136 | "name": "Registration", 137 | "type": "variant", 138 | "priority": 4 139 | }, 140 | { 141 | "id": "login", 142 | "name": "Login", 143 | "type": "variant", 144 | "priority": 3 145 | }, 146 | { 147 | "id": "email_triggering", 148 | "name": "Email-Triggering", 149 | "type": "variant", 150 | "priority": 4 151 | } 152 | ] 153 | }, 154 | { 155 | "id": "unsafe_file_upload", 156 | "name": "Unsafe File Upload", 157 | "type": "subcategory", 158 | "children": [ 159 | { 160 | "id": "no_antivirus", 161 | "name": "No Antivirus", 162 | "type": "variant", 163 | "priority": 4 164 | }, 165 | { 166 | "id": "no_size_limit", 167 | "name": "No Size Limit", 168 | "type": "variant", 169 | "priority": 4 170 | }, 171 | { 172 | "id": "file_extension_filter_bypass", 173 | "name": "File Extension Filter Bypass", 174 | "type": "variant", 175 | "priority": 5 176 | } 177 | ] 178 | }, 179 | { 180 | "id": "missing_secure_or_httponly_cookie_flag", 181 | "name": "Missing Secure or HTTPOnly Cookie Flag", 182 | "type": "subcategory", 183 | "children": [ 184 | { 185 | "id": "session_token", 186 | "name": "Session Token", 187 | "type": "variant", 188 | "priority": 4 189 | }, 190 | { 191 | "id": "non_session_cookie", 192 | "name": "Non-Session Cookie", 193 | "type": "variant", 194 | "priority": 5 195 | } 196 | ] 197 | }, 198 | { 199 | "id": "clickjacking", 200 | "name": "Clickjacking", 201 | "type": "subcategory", 202 | "children": [ 203 | { 204 | "id": "sensitive_action", 205 | "name": "Sensitive Action", 206 | "type": "variant", 207 | "priority": 4 208 | }, 209 | { 210 | "id": "non_sensitive_action", 211 | "name": "Non-Sensitive Action", 212 | "type": "variant", 213 | "priority": 5 214 | } 215 | ] 216 | }, 217 | { 218 | "id": "oauth_misconfiguration", 219 | "name": "OAuth Misconfiguration", 220 | "type": "subcategory", 221 | "children": [ 222 | { 223 | "id": "missing_state_parameter", 224 | "name": "Missing State Parameter", 225 | "type": "variant", 226 | "priority": 4 227 | } 228 | ] 229 | }, 230 | { 231 | "id": "captcha_bypass", 232 | "name": "Captcha Bypass", 233 | "type": "subcategory", 234 | "children": [ 235 | { 236 | "id": "implementation_vulnerability", 237 | "name": "Implementation Vulnerability", 238 | "type": "variant", 239 | "priority": 4 240 | }, 241 | { 242 | "id": "brute_force", 243 | "name": "Brute Force", 244 | "type": "variant", 245 | "priority": 5 246 | } 247 | ] 248 | }, 249 | { 250 | "id": "exposed_admin_portal", 251 | "name": "Exposed Admin Portal", 252 | "type": "subcategory", 253 | "children": [ 254 | { 255 | "id": "to_internet", 256 | "name": "To Internet", 257 | "type": "variant", 258 | "priority": 5 259 | } 260 | ] 261 | }, 262 | { 263 | "id": "missing_dnssec", 264 | "name": "Missing DNSSEC", 265 | "type": "subcategory", 266 | "priority": 5 267 | }, 268 | { 269 | "id": "username_enumeration", 270 | "name": "Username Enumeration", 271 | "type": "subcategory", 272 | "children": [ 273 | { 274 | "id": "brute_force", 275 | "name": "Brute Force", 276 | "type": "variant", 277 | "priority": 5 278 | } 279 | ] 280 | }, 281 | { 282 | "id": "potentially_unsafe_http_method_enabled", 283 | "name": "Potentially Unsafe HTTP Method Enabled", 284 | "type": "subcategory", 285 | "children": [ 286 | { 287 | "id": "options", 288 | "name": "OPTIONS", 289 | "type": "variant", 290 | "priority": 5 291 | }, 292 | { 293 | "id": "trace", 294 | "name": "TRACE", 295 | "type": "variant", 296 | "priority": 5 297 | } 298 | ] 299 | }, 300 | { 301 | "id": "insecure_ssl", 302 | "name": "Insecure SSL", 303 | "type": "subcategory", 304 | "children": [ 305 | { 306 | "id": "lack_of_forward_secrecy", 307 | "name": "Lack of Forward Secrecy", 308 | "type": "variant", 309 | "priority": 5 310 | }, 311 | { 312 | "id": "insecure_cipher_suite", 313 | "name": "Insecure Cipher Suite", 314 | "type": "variant", 315 | "priority": 5 316 | } 317 | ] 318 | }, 319 | { 320 | "id": "lack_of_security_headers", 321 | "name": "Lack of Security Headers", 322 | "type": "subcategory", 323 | "children": [ 324 | { 325 | "id": "x_frame_options", 326 | "name": "X-Frame-Options", 327 | "type": "variant", 328 | "priority": 5 329 | }, 330 | { 331 | "id": "cache_control_for_a_non_sensitive_page", 332 | "name": "Cache-Control for a Non-Sensitive Page", 333 | "type": "variant", 334 | "priority": 5 335 | }, 336 | { 337 | "id": "x_xss_protection", 338 | "name": "X-XSS-Protection", 339 | "type": "variant", 340 | "priority": 5 341 | }, 342 | { 343 | "id": "strict_transport_security", 344 | "name": "Strict-Transport-Security", 345 | "type": "variant", 346 | "priority": 5 347 | }, 348 | { 349 | "id": "x_content_type_options", 350 | "name": "X-Content-Type-Options", 351 | "type": "variant", 352 | "priority": 5 353 | }, 354 | { 355 | "id": "content_security_policy", 356 | "name": "Content-Security-Policy", 357 | "type": "variant", 358 | "priority": 5 359 | }, 360 | { 361 | "id": "public_key_pins", 362 | "name": "Public-Key-Pins", 363 | "type": "variant", 364 | "priority": 5 365 | }, 366 | { 367 | "id": "x_content_security_policy", 368 | "name": "X-Content-Security-Policy", 369 | "type": "variant", 370 | "priority": 5 371 | }, 372 | { 373 | "id": "x_webkit_csp", 374 | "name": "X-Webkit-CSP", 375 | "type": "variant", 376 | "priority": 5 377 | }, 378 | { 379 | "id": "content_security_policy_report_only", 380 | "name": "Content-Security-Policy-Report-Only", 381 | "type": "variant", 382 | "priority": 5 383 | }, 384 | { 385 | "id": "cache_control_for_a_sensitive_page", 386 | "name": "Cache-Control for a Sensitive Page", 387 | "type": "variant", 388 | "priority": 4 389 | } 390 | ] 391 | } 392 | ] 393 | }, 394 | { 395 | "id": "server_side_injection", 396 | "name": "Server-Side Injection", 397 | "type": "category", 398 | "children": [ 399 | { 400 | "id": "file_inclusion", 401 | "name": "File Inclusion", 402 | "type": "subcategory", 403 | "children": [ 404 | { 405 | "id": "local", 406 | "name": "Local", 407 | "type": "variant", 408 | "priority": 1 409 | } 410 | ] 411 | }, 412 | { 413 | "id": "parameter_pollution", 414 | "name": "Parameter Pollution", 415 | "type": "subcategory", 416 | "children": [ 417 | { 418 | "id": "social_media_sharing_buttons", 419 | "name": "Social Media Sharing Buttons", 420 | "type": "variant", 421 | "priority": 5 422 | } 423 | ] 424 | }, 425 | { 426 | "id": "remote_code_execution_rce", 427 | "name": "Remote Code Execution (RCE)", 428 | "type": "subcategory", 429 | "priority": 1 430 | }, 431 | { 432 | "id": "sql_injection", 433 | "name": "SQL Injection", 434 | "type": "subcategory", 435 | "children": [ 436 | { 437 | "id": "error_based", 438 | "name": "Error-Based", 439 | "type": "variant", 440 | "priority": 1 441 | }, 442 | { 443 | "id": "blind", 444 | "name": "Blind", 445 | "type": "variant", 446 | "priority": 1 447 | } 448 | ] 449 | }, 450 | { 451 | "id": "xml_external_entity_injection_xxe", 452 | "name": "XML External Entity Injection (XXE)", 453 | "type": "subcategory", 454 | "priority": 1 455 | }, 456 | { 457 | "id": "http_response_manipulation", 458 | "name": "HTTP Response Manipulation", 459 | "type": "subcategory", 460 | "children": [ 461 | { 462 | "id": "response_splitting_crlf", 463 | "name": "Response Splitting (CRLF)", 464 | "type": "variant", 465 | "priority": 3 466 | } 467 | ] 468 | }, 469 | { 470 | "id": "content_spoofing", 471 | "name": "Content Spoofing", 472 | "type": "subcategory", 473 | "children": [ 474 | { 475 | "id": "iframe_injection", 476 | "name": "iframe Injection", 477 | "type": "variant", 478 | "priority": 3 479 | }, 480 | { 481 | "id": "external_authentication_injection", 482 | "name": "External Authentication Injection", 483 | "type": "variant", 484 | "priority": 4 485 | }, 486 | { 487 | "id": "email_html_injection", 488 | "name": "Email HTML Injection", 489 | "type": "variant", 490 | "priority": 4 491 | }, 492 | { 493 | "id": "text_injection", 494 | "name": "Text Injection", 495 | "type": "variant", 496 | "priority": 5 497 | }, 498 | { 499 | "id": "homograph_idn_based", 500 | "name": "Homograph/IDN-Based", 501 | "type": "variant", 502 | "priority": 5 503 | } 504 | ] 505 | } 506 | ] 507 | }, 508 | { 509 | "id": "broken_authentication_and_session_management", 510 | "name": "Broken Authentication and Session Management", 511 | "type": "category", 512 | "children": [ 513 | { 514 | "id": "authentication_bypass", 515 | "name": "Authentication Bypass", 516 | "type": "subcategory", 517 | "children": [ 518 | { 519 | "id": "vertical", 520 | "name": "Vertical", 521 | "type": "variant", 522 | "priority": 1 523 | }, 524 | { 525 | "id": "horizontal", 526 | "name": "Horizontal", 527 | "type": "variant", 528 | "priority": 2 529 | } 530 | ] 531 | }, 532 | { 533 | "id": "weak_login_function", 534 | "name": "Weak Login Function", 535 | "type": "subcategory", 536 | "children": [ 537 | { 538 | "id": "over_http", 539 | "name": "Over HTTP", 540 | "type": "variant", 541 | "priority": 3 542 | } 543 | ] 544 | }, 545 | { 546 | "id": "session_fixation", 547 | "name": "Session Fixation", 548 | "type": "subcategory", 549 | "priority": 3 550 | }, 551 | { 552 | "id": "failure_to_invalidate_session", 553 | "name": "Failure to Invalidate Session", 554 | "type": "subcategory", 555 | "children": [ 556 | { 557 | "id": "on_logout", 558 | "name": "On Logout", 559 | "type": "variant", 560 | "priority": 4 561 | }, 562 | { 563 | "id": "on_password_reset", 564 | "name": "On Password Reset", 565 | "type": "variant", 566 | "priority": 4 567 | }, 568 | { 569 | "id": "on_password_change", 570 | "name": "On Password Change", 571 | "type": "variant", 572 | "priority": 4 573 | }, 574 | { 575 | "id": "all_sessions", 576 | "name": "All Sessions", 577 | "type": "variant", 578 | "priority": 5 579 | }, 580 | { 581 | "id": "on_email_change", 582 | "name": "On Email Change", 583 | "type": "variant", 584 | "priority": 5 585 | }, 586 | { 587 | "id": "long_timeout", 588 | "name": "Long Timeout", 589 | "type": "variant", 590 | "priority": 5 591 | } 592 | ] 593 | }, 594 | { 595 | "id": "session_token_in_url", 596 | "name": "Session Token in URL", 597 | "type": "subcategory", 598 | "children": [ 599 | { 600 | "id": "over_http", 601 | "name": "Over HTTP", 602 | "type": "variant", 603 | "priority": 4 604 | }, 605 | { 606 | "id": "over_https", 607 | "name": "Over HTTPS", 608 | "type": "variant", 609 | "priority": 5 610 | } 611 | ] 612 | }, 613 | { 614 | "id": "concurrent_logins", 615 | "name": "Concurrent Logins", 616 | "type": "subcategory", 617 | "priority": 5 618 | }, 619 | { 620 | "id": "weak_registration_implementation", 621 | "name": "Weak Registration Implementation", 622 | "type": "subcategory", 623 | "children": [ 624 | { 625 | "id": "over_http", 626 | "name": "Over HTTP", 627 | "type": "variant", 628 | "priority": 4 629 | } 630 | ] 631 | } 632 | ] 633 | }, 634 | { 635 | "id": "insecure_direct_object_references_idor", 636 | "name": "Insecure Direct Object References (IDOR)", 637 | "type": "category", 638 | "priority": null 639 | }, 640 | { 641 | "id": "sensitive_data_exposure", 642 | "name": "Sensitive Data Exposure", 643 | "type": "category", 644 | "children": [ 645 | { 646 | "id": "critically_sensitive_data", 647 | "name": "Critically Sensitive Data", 648 | "type": "subcategory", 649 | "children": [ 650 | { 651 | "id": "password_disclosure", 652 | "name": "Password Disclosure", 653 | "type": "variant", 654 | "priority": 1 655 | }, 656 | { 657 | "id": "private_api_keys", 658 | "name": "Private API Keys", 659 | "type": "variant", 660 | "priority": 1 661 | } 662 | ] 663 | }, 664 | { 665 | "id": "exif_geolocation_data_not_stripped_from_uploaded_images", 666 | "name": "EXIF Geolocation Data Not Stripped From Uploaded Images", 667 | "type": "subcategory", 668 | "children": [ 669 | { 670 | "id": "automatic_user_enumeration", 671 | "name": "Automatic User Enumeration", 672 | "type": "variant", 673 | "priority": 3 674 | }, 675 | { 676 | "id": "manual_user_enumeration", 677 | "name": "Manual User Enumeration", 678 | "type": "variant", 679 | "priority": 4 680 | } 681 | ] 682 | }, 683 | { 684 | "id": "visible_detailed_error_page", 685 | "name": "Visible Detailed Error Page", 686 | "type": "subcategory", 687 | "priority": null 688 | }, 689 | { 690 | "id": "disclosure_of_known_public_information", 691 | "name": "Disclosure of Known Public Information", 692 | "type": "subcategory", 693 | "priority": 5 694 | }, 695 | { 696 | "id": "token_leakage_via_referer", 697 | "name": "Token Leakage via Referer", 698 | "type": "subcategory", 699 | "children": [ 700 | { 701 | "id": "over_https", 702 | "name": "Over HTTPS", 703 | "type": "variant", 704 | "priority": 5 705 | }, 706 | { 707 | "id": "over_http", 708 | "name": "Over HTTP", 709 | "type": "variant", 710 | "priority": 4 711 | } 712 | ] 713 | }, 714 | { 715 | "id": "sensitive_token_in_url", 716 | "name": "Sensitive Token in URL", 717 | "type": "subcategory", 718 | "priority": 4 719 | }, 720 | { 721 | "id": "weak_password_reset_implementation", 722 | "name": "Weak Password Reset Implementation", 723 | "type": "subcategory", 724 | "children": [ 725 | { 726 | "id": "password_reset_token_sent_over_http", 727 | "name": "Password Reset Token Sent Over HTTP", 728 | "type": "variant", 729 | "priority": 4 730 | } 731 | ] 732 | }, 733 | { 734 | "id": "mixed_content", 735 | "name": "Mixed Content", 736 | "type": "subcategory", 737 | "children": [ 738 | { 739 | "id": "sensitive_data_disclosure", 740 | "name": "Sensitive Data Disclosure", 741 | "type": "variant", 742 | "priority": 4 743 | }, 744 | { 745 | "id": "requires_being_a_man_in_the_middle", 746 | "name": "Requires Being a Man-in-the-Middle", 747 | "type": "variant", 748 | "priority": 5 749 | } 750 | ] 751 | }, 752 | { 753 | "id": "sensitive_data_hardcoded", 754 | "name": "Sensitive Data Hardcoded", 755 | "type": "subcategory", 756 | "children": [ 757 | { 758 | "id": "oauth_secret", 759 | "name": "OAuth Secret", 760 | "type": "variant", 761 | "priority": 5 762 | }, 763 | { 764 | "id": "file_paths", 765 | "name": "File Paths", 766 | "type": "variant", 767 | "priority": 5 768 | } 769 | ] 770 | }, 771 | { 772 | "id": "non_sensitive_token_in_url", 773 | "name": "Non-Sensitive Token in URL", 774 | "type": "subcategory", 775 | "priority": 5 776 | } 777 | ] 778 | }, 779 | { 780 | "id": "cross_site_scripting_xss", 781 | "name": "Cross-Site Scripting (XSS)", 782 | "type": "category", 783 | "children": [ 784 | { 785 | "id": "stored", 786 | "name": "Stored", 787 | "type": "subcategory", 788 | "children": [ 789 | { 790 | "id": "non_admin_to_anyone", 791 | "name": "Non-Admin to Anyone", 792 | "type": "variant", 793 | "priority": 2 794 | }, 795 | { 796 | "id": "admin_to_anyone", 797 | "name": "Admin to Anyone", 798 | "type": "variant", 799 | "priority": 3 800 | }, 801 | { 802 | "id": "self", 803 | "name": "Self", 804 | "type": "variant", 805 | "priority": 5 806 | } 807 | ] 808 | }, 809 | { 810 | "id": "reflected", 811 | "name": "Reflected", 812 | "type": "subcategory", 813 | "children": [ 814 | { 815 | "id": "non_admin_to_anyone", 816 | "name": "Non-Admin to Anyone", 817 | "type": "variant", 818 | "priority": 3 819 | }, 820 | { 821 | "id": "admin_to_anyone", 822 | "name": "Admin to Anyone", 823 | "type": "variant", 824 | "priority": 4 825 | }, 826 | { 827 | "id": "self", 828 | "name": "Self", 829 | "type": "variant", 830 | "priority": 5 831 | } 832 | ] 833 | }, 834 | { 835 | "id": "cookie_based", 836 | "name": "Cookie-Based", 837 | "type": "subcategory", 838 | "priority": 4 839 | }, 840 | { 841 | "id": "ie_only", 842 | "name": "IE-Only", 843 | "type": "subcategory", 844 | "children": [ 845 | { 846 | "id": "older_version_ie_10_11", 847 | "name": "Older Version (IE 10/11)", 848 | "type": "variant", 849 | "priority": 4 850 | }, 851 | { 852 | "id": "xss_filter_disabled", 853 | "name": "XSS Filter Disabled", 854 | "type": "variant", 855 | "priority": 5 856 | }, 857 | { 858 | "id": "older_version_ie10", 859 | "name": "Older Version (< IE10)", 860 | "type": "variant", 861 | "priority": 5 862 | } 863 | ] 864 | }, 865 | { 866 | "id": "referer", 867 | "name": "Referer", 868 | "type": "subcategory", 869 | "priority": 4 870 | }, 871 | { 872 | "id": "trace_method", 873 | "name": "TRACE Method", 874 | "type": "subcategory", 875 | "priority": 5 876 | }, 877 | { 878 | "id": "universal_uxss", 879 | "name": "Universal (UXSS)", 880 | "type": "subcategory", 881 | "priority": 4 882 | }, 883 | { 884 | "id": "off_domain", 885 | "name": "Off-Domain", 886 | "type": "subcategory", 887 | "children": [ 888 | { 889 | "id": "data_uri", 890 | "name": "Data URI", 891 | "type": "variant", 892 | "priority": 4 893 | } 894 | ] 895 | } 896 | ] 897 | }, 898 | { 899 | "id": "missing_function_level_access_control", 900 | "name": "Missing Function Level Access Control", 901 | "type": "category", 902 | "children": [ 903 | { 904 | "id": "server_side_request_forgery_ssrf", 905 | "name": "Server-Side Request Forgery (SSRF)", 906 | "type": "subcategory", 907 | "children": [ 908 | { 909 | "id": "internal", 910 | "name": "Internal", 911 | "type": "variant", 912 | "priority": 2 913 | }, 914 | { 915 | "id": "external", 916 | "name": "External", 917 | "type": "variant", 918 | "priority": 4 919 | } 920 | ] 921 | }, 922 | { 923 | "id": "username_enumeration", 924 | "name": "Username Enumeration", 925 | "type": "subcategory", 926 | "children": [ 927 | { 928 | "id": "data_leak", 929 | "name": "Data Leak", 930 | "type": "variant", 931 | "priority": 4 932 | } 933 | ] 934 | }, 935 | { 936 | "id": "exposed_sensitive_android_intent", 937 | "name": "Exposed Sensitive Android Intent", 938 | "type": "subcategory", 939 | "priority": null 940 | }, 941 | { 942 | "id": "exposed_sensitive_ios_url_scheme", 943 | "name": "Exposed Sensitive iOS URL Scheme", 944 | "type": "subcategory", 945 | "priority": null 946 | } 947 | ] 948 | }, 949 | { 950 | "id": "cross_site_request_forgery_csrf", 951 | "name": "Cross-Site Request Forgery (CSRF)", 952 | "type": "category", 953 | "priority": null 954 | }, 955 | { 956 | "id": "application_level_denial_of_service_dos", 957 | "name": "Application-Level Denial-of-Service (DoS)", 958 | "type": "category", 959 | "children": [ 960 | { 961 | "id": "critical_impact_and_or_easy_difficulty", 962 | "name": "Critical Impact and/or Easy Difficulty", 963 | "type": "subcategory", 964 | "priority": 2 965 | }, 966 | { 967 | "id": "high_impact_and_or_medium_difficulty", 968 | "name": "High Impact and/or Medium Difficulty", 969 | "type": "subcategory", 970 | "priority": 3 971 | }, 972 | { 973 | "id": "app_crash", 974 | "name": "App Crash", 975 | "type": "subcategory", 976 | "children": [ 977 | { 978 | "id": "malformed_android_intents", 979 | "name": "Malformed Android Intents", 980 | "type": "variant", 981 | "priority": 5 982 | }, 983 | { 984 | "id": "malformed_ios_url_schemes", 985 | "name": "Malformed iOS URL Schemes", 986 | "type": "variant", 987 | "priority": 5 988 | } 989 | ] 990 | } 991 | ] 992 | }, 993 | { 994 | "id": "unvalidated_redirects_and_forwards", 995 | "name": "Unvalidated Redirects and Forwards", 996 | "type": "category", 997 | "children": [ 998 | { 999 | "id": "open_redirect", 1000 | "name": "Open Redirect", 1001 | "type": "subcategory", 1002 | "children": [ 1003 | { 1004 | "id": "get_based_all_users", 1005 | "name": "GET-Based (All Users)", 1006 | "type": "variant", 1007 | "priority": 3 1008 | }, 1009 | { 1010 | "id": "get_based_authenticated", 1011 | "name": "GET-Based (Authenticated)", 1012 | "type": "variant", 1013 | "priority": 4 1014 | }, 1015 | { 1016 | "id": "get_based_unauthenticated", 1017 | "name": "GET-Based (Unauthenticated)", 1018 | "type": "variant", 1019 | "priority": 4 1020 | }, 1021 | { 1022 | "id": "post_based", 1023 | "name": "POST-Based", 1024 | "type": "variant", 1025 | "priority": 5 1026 | }, 1027 | { 1028 | "id": "header_based", 1029 | "name": "Header-Based", 1030 | "type": "variant", 1031 | "priority": 5 1032 | } 1033 | ] 1034 | }, 1035 | { 1036 | "id": "tabnabbing", 1037 | "name": "Tabnabbing", 1038 | "type": "subcategory", 1039 | "priority": 5 1040 | }, 1041 | { 1042 | "id": "lack_of_security_speed_bump_page", 1043 | "name": "Lack of Security Speed Bump Page", 1044 | "type": "subcategory", 1045 | "priority": 5 1046 | } 1047 | ] 1048 | }, 1049 | { 1050 | "id": "external_behavior", 1051 | "name": "External Behavior", 1052 | "type": "category", 1053 | "children": [ 1054 | { 1055 | "id": "browser_feature", 1056 | "name": "Browser Feature", 1057 | "type": "subcategory", 1058 | "children": [ 1059 | { 1060 | "id": "plaintext_password_field", 1061 | "name": "Plaintext Password Field", 1062 | "type": "variant", 1063 | "priority": 5 1064 | }, 1065 | { 1066 | "id": "save_password", 1067 | "name": "Save Password", 1068 | "type": "variant", 1069 | "priority": 5 1070 | }, 1071 | { 1072 | "id": "autocomplete_enabled", 1073 | "name": "Autocomplete Enabled", 1074 | "type": "variant", 1075 | "priority": 5 1076 | }, 1077 | { 1078 | "id": "autocorrect_enabled", 1079 | "name": "Autocorrect Enabled", 1080 | "type": "variant", 1081 | "priority": 5 1082 | }, 1083 | { 1084 | "id": "aggressive_offline_caching", 1085 | "name": "Aggressive Offline Caching", 1086 | "type": "variant", 1087 | "priority": 5 1088 | } 1089 | ] 1090 | }, 1091 | { 1092 | "id": "csv_injection", 1093 | "name": "CSV Injection", 1094 | "type": "subcategory", 1095 | "priority": 5 1096 | }, 1097 | { 1098 | "id": "captcha_bypass", 1099 | "name": "Captcha Bypass", 1100 | "type": "subcategory", 1101 | "children": [ 1102 | { 1103 | "id": "crowdsourcing", 1104 | "name": "Crowdsourcing", 1105 | "type": "variant", 1106 | "priority": 5 1107 | } 1108 | ] 1109 | }, 1110 | { 1111 | "id": "system_clipboard_leak", 1112 | "name": "System Clipboard Leak", 1113 | "type": "subcategory", 1114 | "children": [ 1115 | { 1116 | "id": "shared_links", 1117 | "name": "Shared Links", 1118 | "type": "variant", 1119 | "priority": 5 1120 | } 1121 | ] 1122 | }, 1123 | { 1124 | "id": "user_password_persisted_in_memory", 1125 | "name": "User Password Persisted in Memory", 1126 | "type": "subcategory", 1127 | "priority": 5 1128 | } 1129 | ] 1130 | }, 1131 | { 1132 | "id": "insufficient_security_configurability", 1133 | "name": "Insufficient Security Configurability", 1134 | "type": "category", 1135 | "children": [ 1136 | { 1137 | "id": "weak_password_policy", 1138 | "name": "Weak Password Policy", 1139 | "type": "subcategory", 1140 | "children": [ 1141 | { 1142 | "id": "complexity_both_length_and_char_type_not_enforced", 1143 | "name": "Complexity, Both Length and Char Type Not Enforced", 1144 | "type": "variant", 1145 | "priority": 3 1146 | }, 1147 | { 1148 | "id": "complexity_length_not_enforced", 1149 | "name": "Complexity, Length Not Enforced", 1150 | "type": "variant", 1151 | "priority": 4 1152 | }, 1153 | { 1154 | "id": "complexity_char_type_not_enforced", 1155 | "name": "Complexity, Char Type Not Enforced", 1156 | "type": "variant", 1157 | "priority": 4 1158 | }, 1159 | { 1160 | "id": "allows_reuse_of_old_passwords", 1161 | "name": "Allows Reuse of Old Passwords", 1162 | "type": "variant", 1163 | "priority": 5 1164 | }, 1165 | { 1166 | "id": "allows_password_to_be_same_as_email_username", 1167 | "name": "Allows Password to be Same as Email/Username", 1168 | "type": "variant", 1169 | "priority": 5 1170 | } 1171 | ] 1172 | }, 1173 | { 1174 | "id": "weak_password_reset_implementation", 1175 | "name": "Weak Password Reset Implementation", 1176 | "type": "subcategory", 1177 | "children": [ 1178 | { 1179 | "id": "token_is_not_invalidated_after_use", 1180 | "name": "Token is Not Invalidated After Use", 1181 | "type": "variant", 1182 | "priority": 4 1183 | }, 1184 | { 1185 | "id": "token_is_not_invalidated_after_email_change", 1186 | "name": "Token is Not Invalidated After Email Change", 1187 | "type": "variant", 1188 | "priority": 5 1189 | }, 1190 | { 1191 | "id": "token_is_not_invalidated_after_password_change", 1192 | "name": "Token is Not Invalidated After Password Change", 1193 | "type": "variant", 1194 | "priority": 5 1195 | }, 1196 | { 1197 | "id": "token_has_long_timed_expiry", 1198 | "name": "Token Has Long Timed Expiry", 1199 | "type": "variant", 1200 | "priority": 5 1201 | }, 1202 | { 1203 | "id": "token_is_not_invalidated_after_new_token_is_requested", 1204 | "name": "Token is Not Invalidated After New Token is Requested", 1205 | "type": "variant", 1206 | "priority": 5 1207 | } 1208 | ] 1209 | }, 1210 | { 1211 | "id": "lack_of_verification_email", 1212 | "name": "Lack of Verification Email", 1213 | "type": "subcategory", 1214 | "priority": 5 1215 | }, 1216 | { 1217 | "id": "lack_of_notification_email", 1218 | "name": "Lack of Notification Email", 1219 | "type": "subcategory", 1220 | "priority": 5 1221 | }, 1222 | { 1223 | "id": "weak_registration_implementation", 1224 | "name": "Weak Registration Implementation", 1225 | "type": "subcategory", 1226 | "children": [ 1227 | { 1228 | "id": "allows_disposable_email_addresses", 1229 | "name": "Allows Disposable Email Addresses", 1230 | "type": "variant", 1231 | "priority": 5 1232 | } 1233 | ] 1234 | }, 1235 | { 1236 | "id": "weak_2fa_implementation", 1237 | "name": "Weak 2FA Implementation", 1238 | "type": "subcategory", 1239 | "children": [ 1240 | { 1241 | "id": "missing_failsafe", 1242 | "name": "Missing Failsafe", 1243 | "type": "variant", 1244 | "priority": 5 1245 | } 1246 | ] 1247 | } 1248 | ] 1249 | }, 1250 | { 1251 | "id": "using_components_with_known_vulnerabilities", 1252 | "name": "Using Components with Known Vulnerabilities", 1253 | "type": "category", 1254 | "children": [ 1255 | { 1256 | "id": "rosetta_flash", 1257 | "name": "Rosetta Flash", 1258 | "type": "subcategory", 1259 | "priority": 4 1260 | }, 1261 | { 1262 | "id": "outdated_software_version", 1263 | "name": "Outdated Software Version", 1264 | "type": "subcategory", 1265 | "priority": 5 1266 | }, 1267 | { 1268 | "id": "captcha_bypass", 1269 | "name": "Captcha Bypass", 1270 | "type": "subcategory", 1271 | "children": [ 1272 | { 1273 | "id": "ocr_optical_character_recognition", 1274 | "name": "OCR (Optical Character Recognition)", 1275 | "type": "variant", 1276 | "priority": 5 1277 | } 1278 | ] 1279 | } 1280 | ] 1281 | }, 1282 | { 1283 | "id": "insecure_data_storage", 1284 | "name": "Insecure Data Storage", 1285 | "type": "category", 1286 | "children": [ 1287 | { 1288 | "id": "credentials_stored_unencrypted", 1289 | "name": "Credentials Stored Unencrypted", 1290 | "type": "subcategory", 1291 | "children": [ 1292 | { 1293 | "id": "on_external_storage", 1294 | "name": "On External Storage", 1295 | "type": "variant", 1296 | "priority": 4 1297 | }, 1298 | { 1299 | "id": "on_internal_storage", 1300 | "name": "On Internal Storage", 1301 | "type": "variant", 1302 | "priority": 5 1303 | } 1304 | ] 1305 | }, 1306 | { 1307 | "id": "sensitive_application_data_stored_unencrypted", 1308 | "name": "Sensitive Application Data Stored Unencrypted", 1309 | "type": "subcategory", 1310 | "children": [ 1311 | { 1312 | "id": "on_external_storage", 1313 | "name": "On External Storage", 1314 | "type": "variant", 1315 | "priority": 4 1316 | }, 1317 | { 1318 | "id": "on_internal_storage", 1319 | "name": "On Internal Storage", 1320 | "type": "variant", 1321 | "priority": 5 1322 | } 1323 | ] 1324 | }, 1325 | { 1326 | "id": "non_sensitive_application_data_stored_unencrypted", 1327 | "name": "Non-Sensitive Application Data Stored Unencrypted", 1328 | "type": "subcategory", 1329 | "priority": 5 1330 | }, 1331 | { 1332 | "id": "screen_caching_enabled", 1333 | "name": "Screen Caching Enabled", 1334 | "type": "subcategory", 1335 | "priority": 5 1336 | }, 1337 | { 1338 | "id": "insecure_data_storage", 1339 | "name": "Insecure Data Storage", 1340 | "type": "subcategory", 1341 | "children": [ 1342 | { 1343 | "id": "password", 1344 | "name": "Password", 1345 | "type": "variant", 1346 | "priority": 2 1347 | } 1348 | ] 1349 | } 1350 | ] 1351 | }, 1352 | { 1353 | "id": "lack_of_binary_hardening", 1354 | "name": "Lack of Binary Hardening", 1355 | "type": "category", 1356 | "children": [ 1357 | { 1358 | "id": "lack_of_exploit_mitigations", 1359 | "name": "Lack of Exploit Mitigations", 1360 | "type": "subcategory", 1361 | "priority": 5 1362 | }, 1363 | { 1364 | "id": "lack_of_jailbreak_detection", 1365 | "name": "Lack of Jailbreak Detection", 1366 | "type": "subcategory", 1367 | "priority": 5 1368 | }, 1369 | { 1370 | "id": "lack_of_obfuscation", 1371 | "name": "Lack of Obfuscation", 1372 | "type": "subcategory", 1373 | "priority": 5 1374 | }, 1375 | { 1376 | "id": "runtime_instrumentation_based", 1377 | "name": "Runtime Instrumentation-Based", 1378 | "type": "subcategory", 1379 | "priority": 5 1380 | } 1381 | ] 1382 | }, 1383 | { 1384 | "id": "insecure_data_transport", 1385 | "name": "Insecure Data Transport", 1386 | "type": "category", 1387 | "children": [ 1388 | { 1389 | "id": "ssl_certificate_pinning", 1390 | "name": "SSL Certificate Pinning", 1391 | "type": "subcategory", 1392 | "children": [ 1393 | { 1394 | "id": "absent", 1395 | "name": "Absent", 1396 | "type": "variant", 1397 | "priority": 5 1398 | }, 1399 | { 1400 | "id": "defeatable", 1401 | "name": "Defeatable", 1402 | "type": "variant", 1403 | "priority": 5 1404 | } 1405 | ] 1406 | } 1407 | ] 1408 | }, 1409 | { 1410 | "id": "insecure_os_firmware", 1411 | "name": "Insecure OS/Firmware", 1412 | "type": "category", 1413 | "children": [ 1414 | { 1415 | "id": "command_injection", 1416 | "name": "Command Injection", 1417 | "type": "subcategory", 1418 | "priority": 1 1419 | }, 1420 | { 1421 | "id": "hardcoded_password", 1422 | "name": "Hardcoded Password", 1423 | "type": "subcategory", 1424 | "children": [ 1425 | { 1426 | "id": "privileged_user", 1427 | "name": "Privileged User", 1428 | "type": "variant", 1429 | "priority": 1 1430 | }, 1431 | { 1432 | "id": "non_privileged_user", 1433 | "name": "Non-Privileged User", 1434 | "type": "variant", 1435 | "priority": 2 1436 | } 1437 | ] 1438 | } 1439 | ] 1440 | }, 1441 | { 1442 | "id": "broken_cryptography", 1443 | "name": "Broken Cryptography", 1444 | "type": "category", 1445 | "children": [ 1446 | { 1447 | "id": "cryptographic_flaw", 1448 | "name": "Cryptographic Flaw", 1449 | "type": "subcategory", 1450 | "children": [ 1451 | { 1452 | "id": "incorrect_usage", 1453 | "name": "Incorrect Usage", 1454 | "type": "variant", 1455 | "priority": 1 1456 | } 1457 | ] 1458 | } 1459 | ] 1460 | }, 1461 | { 1462 | "id": "privacy_concerns", 1463 | "name": "Privacy Concerns", 1464 | "type": "category", 1465 | "children": [ 1466 | { 1467 | "id": "unnecessary_data_collection", 1468 | "name": "Unnecessary Data Collection", 1469 | "type": "subcategory", 1470 | "children": [ 1471 | { 1472 | "id": "wifi_ssid_password", 1473 | "name": "WiFi SSID+Password", 1474 | "type": "variant", 1475 | "priority": 4 1476 | } 1477 | ] 1478 | } 1479 | ] 1480 | }, 1481 | { 1482 | "id": "network_security_misconfiguration", 1483 | "name": "Network Security Misconfiguration", 1484 | "type": "category", 1485 | "children": [ 1486 | { 1487 | "id": "telnet_enabled", 1488 | "name": "Telnet Enabled", 1489 | "type": "subcategory", 1490 | "children": [ 1491 | { 1492 | "id": "credentials_required", 1493 | "name": "Credentials Required", 1494 | "type": "variant", 1495 | "priority": 4 1496 | } 1497 | ] 1498 | } 1499 | ] 1500 | }, 1501 | { 1502 | "id": "mobile_security_misconfiguration", 1503 | "name": "Mobile Security Misconfiguration", 1504 | "type": "category", 1505 | "priority": null 1506 | }, 1507 | { 1508 | "id": "poor_physical_security", 1509 | "name": "Poor Physical Security", 1510 | "type": "category", 1511 | "priority": null 1512 | }, 1513 | { 1514 | "id": "social_engineering", 1515 | "name": "Social Engineering", 1516 | "type": "category", 1517 | "priority": null 1518 | }, 1519 | { 1520 | "id": "client_side_injection", 1521 | "name": "Client-Side Injection", 1522 | "type": "category", 1523 | "priority": null 1524 | } 1525 | ] 1526 | } 1527 | -------------------------------------------------------------------------------- /spec/sample_vrt/2.0/deprecated-node-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "poor_physical_security": { 3 | "2.0": "other" 4 | }, 5 | "social_engineering": { 6 | "2.0": "other" 7 | }, 8 | "unvalidated_redirects_and_forwards.open_redirect.get_based_all_users": { 9 | "2.0": "unvalidated_redirects_and_forwards.open_redirect.get_based_thingo" 10 | }, 11 | "unvalidated_redirects_and_forwards.open_redirect.get_based_authenticated": { 12 | "2.0": "unvalidated_redirects_and_forwards.open_redirect.get_based" 13 | }, 14 | "unvalidated_redirects_and_forwards.open_redirect.get_based_unauthenticated": { 15 | "2.0": "unvalidated_redirects_and_forwards.open_redirect.get_based" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec/sample_vrt/2.0/mappings/cvss_v3.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": "default" 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "cvss_v3": "a", 9 | "children": [ 10 | { 11 | "id": "unsafe_cross_origin_resource_sharing", 12 | "cvss_v3": "b" 13 | }, 14 | { 15 | "id": "ssl_attack_breach_poodle_etc", 16 | "cvss_v3": "c" 17 | }, 18 | { 19 | "id": "using_default_credentials", 20 | "cvss_v3": "d" 21 | }, 22 | { 23 | "id": "misconfigured_dns", 24 | "cvss_v3": "e" 25 | } 26 | ] 27 | }, 28 | { 29 | "id": "server_side_injection", 30 | "children": [ 31 | { 32 | "id": "file_inclusion", 33 | "cvss_v3": "f" 34 | }, 35 | { 36 | "id": "remote_code_execution_rce", 37 | "cvss_v3": "g" 38 | }, 39 | { 40 | "id": "sql_injection", 41 | "cvss_v3": "h" 42 | } 43 | ] 44 | }, 45 | { 46 | "id": "unvalidated_redirects_and_forwards", 47 | "cvss_v3": "i", 48 | "children": [ 49 | { 50 | "id": "open_redirect", 51 | "children": [ 52 | { 53 | "id": "get_based", 54 | "cvss_v3": "j" 55 | } 56 | ] 57 | } 58 | ] 59 | }, 60 | { 61 | "id": "dumb_v2_category", 62 | "cvss_v3": "k" 63 | }, 64 | { 65 | "id": "insecure_data_storage", 66 | "cvss_v3": "l" 67 | }, 68 | { 69 | "id": "broken_authentication_and_session_management", 70 | "children": [ 71 | { 72 | "id": "authentication_bypass", 73 | "cvss_v3": "m" 74 | } 75 | ] 76 | } 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /spec/sample_vrt/2.0/mappings/cwe.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": ["CWE-2000"] 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "cwe": ["CWE-933"], 9 | "children": [ 10 | { 11 | "id": "unsafe_cross_origin_resource_sharing", 12 | "cwe": ["CWE-942"] 13 | }, 14 | { 15 | "id": "path_traversal", 16 | "cwe": ["CWE-22", "CWE-73"] 17 | }, 18 | { 19 | "id": "directory_listing_enabled", 20 | "cwe": ["CWE-548"] 21 | }, 22 | { 23 | "id": "ssl_attack_breach_poodle_etc", 24 | "cwe": ["CWE-310"] 25 | }, 26 | { 27 | "id": "using_default_credentials", 28 | "cwe": ["CWE-255", "CWE-521"] 29 | }, 30 | { 31 | "id": "misconfigured_dns", 32 | "children": [ 33 | { 34 | "id": "zone_transfer", 35 | "cwe": ["CWE-669"] 36 | } 37 | ] 38 | }, 39 | { 40 | "id": "dbms_misconfiguration", 41 | "children": [ 42 | { 43 | "id": "excessively_privileged_user_dba", 44 | "cwe": ["CWE-250"] 45 | } 46 | ] 47 | }, 48 | { 49 | "id": "lack_of_password_confirmation", 50 | "children": [ 51 | { 52 | "id": "change_password", 53 | "cwe": ["CWE-620"] 54 | } 55 | ] 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /spec/sample_vrt/2.0/mappings/remediation_advice.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": null, 4 | "keys": ["remediation_advice", "references"] 5 | }, 6 | "content": [ 7 | { 8 | "id": "server_security_misconfiguration", 9 | "references": [ 10 | "https://www.owasp.org/index.php/Top_10_2013-A5-Security_Misconfiguration", 11 | "http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration" 12 | ], 13 | "children": [ 14 | { 15 | "id": "unsafe_cross_origin_resource_sharing", 16 | "remediation_advice": "This is advice", 17 | "references": [ 18 | "https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Cross_Origin_Resource_Sharing", 19 | "https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" 20 | ] 21 | }, 22 | { 23 | "id": "directory_listing_enabled", 24 | "remediation_advice": "Restrict directory listings being displayed from the server configuration. \n\nExample for Apache:\n\n1. Edit the server configuration file or edit/create directory .htaccess\n2. Add the following line:\nOptions -Indexes\n3. If it is the last line, make sure you have a new line after it.", 25 | "references": [ 26 | "http://projects.webappsec.org/w/page/13246922/Directory%20Indexing" 27 | ] 28 | }, 29 | { 30 | "id": "misconfigured_dns", 31 | "children": [ 32 | { 33 | "id": "subdomain_takeover", 34 | "remediation_advice": "1. Set up your external service so it fully listens to your wildcard DNS.\n2. Keep your DNS-entries constantly vetted and restricted.", 35 | "references": [ 36 | "https://labs.detectify.com/2014/10/21/hostile-subdomain-takeover-using-herokugithubdesk-more/" 37 | ] 38 | }, 39 | { 40 | "id": "zone_transfer", 41 | "remediation_advice": "Do not allow DNS zone transfers.", 42 | "references": [ 43 | "https://www.sans.org/reading-room/whitepapers/dns/securing-dns-zone-transfer-868", 44 | "https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-1999-0532" 45 | ] 46 | }, 47 | { 48 | "id": "missing_caa_record", 49 | "remediation_advice": "As the domain name holder you can modify the DNS zone file to specify one or more Certification Authorities (CAs) authorized to issue certificates for that domain.", 50 | "references": [ 51 | "https://tools.ietf.org/html/rfc6844" 52 | ] 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /spec/sample_vrt/2.0/third-party-mappings/remediation_training/secure-code-warrior-links.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_security_misconfiguration": null, 3 | "server_security_misconfiguration.unsafe_cross_origin_resource_sharing": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:unsafe_cross_origin_resource_sharing&redirect=true", 4 | "server_security_misconfiguration.path_traversal": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:path_traversal&redirect=true", 5 | "server_security_misconfiguration.directory_listing_enabled": null, 6 | "server_security_misconfiguration.directory_listing_enabled.sensitive_data_exposure": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:directory_listing_enabled:sensitive_data_exposure&redirect=true", 7 | "server_security_misconfiguration.directory_listing_enabled.non_sensitive_data_exposure": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:directory_listing_enabled:non_sensitive_data_exposure&redirect=true", 8 | "server_security_misconfiguration.same_site_scripting": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:same_site_scripting&redirect=true", 9 | "server_security_misconfiguration.ssl_attack_breach_poodle_etc": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:ssl_attack_breach_poodle_etc&redirect=true", 10 | "server_security_misconfiguration.using_default_credentials": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:using_default_credentials&redirect=true", 11 | "server_security_misconfiguration.misconfigured_dns": null, 12 | "server_security_misconfiguration.misconfigured_dns.basic_subdomain_takeover": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:basic_subdomain_takeover&redirect=true", 13 | "server_security_misconfiguration.misconfigured_dns.high_impact_subdomain_takeover": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:high_impact_subdomain_takeover&redirect=true", 14 | "server_security_misconfiguration.misconfigured_dns.zone_transfer": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:zone_transfer&redirect=true", 15 | "server_security_misconfiguration.misconfigured_dns.missing_caa_record": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:missing_caa_record&redirect=true", 16 | "server_security_misconfiguration.mail_server_misconfiguration": null, 17 | "server_security_misconfiguration.mail_server_misconfiguration.no_spoofing_protection_on_email_domain": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:no_spoofing_protection_on_email_domain&redirect=true", 18 | "server_security_misconfiguration.mail_server_misconfiguration.email_spoofing_to_inbox_due_to_missing_or_misconfigured_dmarc_on_email_domain": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:email_spoofing_to_inbox_due_to_missing_or_misconfigured_dmarc_on_email_domain&redirect=true", 19 | "server_security_misconfiguration.mail_server_misconfiguration.email_spoofing_to_spam_folder": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:email_spoofing_to_spam_folder&redirect=true", 20 | "server_security_misconfiguration.mail_server_misconfiguration.missing_or_misconfigured_spf_and_or_dkim": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:missing_or_misconfigured_spf_and_or_dkim&redirect=true", 21 | "server_security_misconfiguration.mail_server_misconfiguration.email_spoofing_on_non_email_domain": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:email_spoofing_on_non_email_domain&redirect=true", 22 | "server_security_misconfiguration.dbms_misconfiguration": null, 23 | "server_security_misconfiguration.dbms_misconfiguration.excessively_privileged_user_dba": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:dbms_misconfiguration:excessively_privileged_user_dba&redirect=true", 24 | "server_security_misconfiguration.lack_of_password_confirmation": null, 25 | "server_security_misconfiguration.lack_of_password_confirmation.change_email_address": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:change_email_address&redirect=true", 26 | "server_security_misconfiguration.lack_of_password_confirmation.change_password": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:change_password&redirect=true", 27 | "server_security_misconfiguration.lack_of_password_confirmation.delete_account": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:delete_account&redirect=true", 28 | "server_security_misconfiguration.lack_of_password_confirmation.manage_two_fa": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:manage_two_fa&redirect=true", 29 | "server_security_misconfiguration.no_rate_limiting_on_form": null, 30 | "server_security_misconfiguration.no_rate_limiting_on_form.registration": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:registration&redirect=true", 31 | "server_security_misconfiguration.no_rate_limiting_on_form.login": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:login&redirect=true", 32 | "server_security_misconfiguration.no_rate_limiting_on_form.email_triggering": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:email_triggering&redirect=true", 33 | "server_security_misconfiguration.no_rate_limiting_on_form.sms_triggering": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:sms_triggering&redirect=true" 34 | } 35 | -------------------------------------------------------------------------------- /spec/sample_vrt/2.0/vulnerability-rating-taxonomy.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "release_date": "2017-02-18T00:00:00+00:00" 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "name": "Server Security Misconfiguration", 9 | "type": "category", 10 | "children": [ 11 | { 12 | "id": "unsafe_cross_origin_resource_sharing", 13 | "name": "Unsafe Cross-Origin Resource Sharing", 14 | "type": "subcategory", 15 | "children": [ 16 | { 17 | "id": "critical_impact", 18 | "name": "Critical Impact", 19 | "type": "variant", 20 | "priority": 1, 21 | "internal_notes": "" 22 | }, 23 | { 24 | "id": "high_impact", 25 | "name": "High Impact", 26 | "type": "variant", 27 | "priority": 2, 28 | "internal_notes": "" 29 | } 30 | ] 31 | }, 32 | { 33 | "id": "ssl_attack_breach_poodle_etc", 34 | "name": "SSL Attack (BREACH, POODLE etc.)", 35 | "type": "subcategory", 36 | "priority": 5, 37 | "internal_notes": "triage based on demonstrated impact - if purely theoretical, it's P5 under this entry" 38 | }, 39 | { 40 | "id": "using_default_credentials", 41 | "name": "Using Default Credentials", 42 | "type": "subcategory" 43 | }, 44 | { 45 | "id": "misconfigured_dns", 46 | "name": "Misconfigured DNS", 47 | "type": "subcategory", 48 | "priority": 2, 49 | "internal_notes": "have to provide a fully working PoC demonstrating hosting their arbitrary content on an in-scope domain" 50 | }, 51 | { 52 | "id": "weak_password_policy", 53 | "name": "Weak Password Policy", 54 | "type": "subcategory", 55 | "priority": 3, 56 | "internal_notes": "" 57 | }, 58 | { 59 | "id": "unsafe_file_upload", 60 | "name": "Unsafe File Upload", 61 | "type": "subcategory", 62 | "children": [ 63 | { 64 | "id": "no_antivirus", 65 | "name": "No Antivirus", 66 | "type": "variant", 67 | "priority": 4, 68 | "internal_notes": "" 69 | }, 70 | { 71 | "id": "no_size_limit", 72 | "name": "No Size Limit", 73 | "type": "variant", 74 | "priority": 4, 75 | "internal_notes": "" 76 | }, 77 | { 78 | "id": "file_extension_filter_bypass", 79 | "name": "File Extension Filter Bypass", 80 | "type": "variant", 81 | "priority": 5, 82 | "internal_notes": "e.g. rename 'file.exe' to 'file.exe.jpg' and it can be uploaded" 83 | } 84 | ] 85 | } 86 | ] 87 | }, 88 | { 89 | "id": "server_side_injection", 90 | "name": "Server-Side Injection", 91 | "type": "category", 92 | "children": [ 93 | { 94 | "id": "file_inclusion", 95 | "name": "File Inclusion", 96 | "type": "subcategory", 97 | "priority": 1, 98 | "internal_notes": "" 99 | }, 100 | { 101 | "id": "remote_code_execution_rce", 102 | "name": "Remote Code Execution (RCE)", 103 | "type": "subcategory", 104 | "priority": 1, 105 | "internal_notes": "" 106 | }, 107 | { 108 | "id": "sql_injection", 109 | "name": "SQL Injection", 110 | "type": "subcategory", 111 | "children": [ 112 | { 113 | "id": "error_based", 114 | "name": "Error-Based", 115 | "type": "variant", 116 | "priority": 1, 117 | "internal_notes": "researcher should be using an innocuous PoC for all SQLi" 118 | }, 119 | { 120 | "id": "blind", 121 | "name": "Blind", 122 | "type": "variant", 123 | "priority": 1, 124 | "internal_notes": "researcher should be using an innocuous PoC for all SQLi" 125 | } 126 | ] 127 | } 128 | 129 | ] 130 | }, 131 | { 132 | "id": "unvalidated_redirects_and_forwards", 133 | "name": "Unvalidated Redirects and Forwards", 134 | "type": "category", 135 | "children": [ 136 | { 137 | "id": "open_redirect", 138 | "name": "Open Redirect", 139 | "type": "subcategory", 140 | "children": [ 141 | { 142 | "id": "get_based", 143 | "name": "GET-Based", 144 | "type": "variant", 145 | "priority": 4 146 | }, 147 | { 148 | "id": "get_based_thingo", 149 | "name": "Just GET-Based things", 150 | "type": "variant", 151 | "priority": 4 152 | } 153 | ] 154 | } 155 | ] 156 | }, 157 | { 158 | "id": "broken_authentication_and_session_management", 159 | "name": "Broken Authentication and Session Management", 160 | "type": "category", 161 | "children": [ 162 | { 163 | "id": "authentication_bypass", 164 | "name": "Authentication Bypass", 165 | "type": "subcategory", 166 | "priority": 1 167 | } 168 | ] 169 | }, 170 | { 171 | "id": "dumb_v2_category", 172 | "name": "Dumb V2 Category", 173 | "type": "category", 174 | "priority": 4, 175 | "children": [] 176 | }, 177 | { 178 | "id": "insecure_data_storage", 179 | "name": "insecure_data_storage", 180 | "type": "category", 181 | "children": [ 182 | { 183 | "id": "insecure_data_storage", 184 | "name": "insecure_data_storage", 185 | "type": "subcategory", 186 | "children": [ 187 | { 188 | "id": "password", 189 | "name": "password", 190 | "type": "variant" 191 | } 192 | ] 193 | } 194 | ] 195 | } 196 | ] 197 | } 198 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/deprecated-node-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "poor_physical_security": { 3 | "2.0": "other" 4 | }, 5 | "social_engineering": { 6 | "2.0": "other" 7 | }, 8 | "unvalidated_redirects_and_forwards.open_redirect.get_based_all_users": { 9 | "2.0": "unvalidated_redirects_and_forwards.open_redirect.get_based_thingo" 10 | }, 11 | "unvalidated_redirects_and_forwards.open_redirect.get_based_authenticated": { 12 | "2.0": "unvalidated_redirects_and_forwards.open_redirect.get_based" 13 | }, 14 | "unvalidated_redirects_and_forwards.open_redirect.get_based_unauthenticated": { 15 | "2.0": "unvalidated_redirects_and_forwards.open_redirect.get_based" 16 | }, 17 | "unvalidated_redirects_and_forwards.open_redirect.get_based_thingo" : { 18 | "999.999": "unvalidated_redirects_and_forwards.open_redirect.get_based" 19 | }, 20 | "unvalidated_redirects_and_forwards.lack_of_security_speed_bump_page": { 21 | "999.999": "external_behavior.lack_of_security_speed_bump_page" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/mappings/cvss_v3/cvss_v3.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", 9 | "children": [ 10 | { 11 | "id": "unsafe_cross_origin_resource_sharing", 12 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N" 13 | }, 14 | { 15 | "id": "ssl_attack_breach_poodle_etc", 16 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N" 17 | }, 18 | { 19 | "id": "using_default_credentials", 20 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N" 21 | }, 22 | { 23 | "id": "misconfigured_dns", 24 | "cvss_v3": "AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:L" 25 | } 26 | ] 27 | }, 28 | { 29 | "id": "server_side_injection", 30 | "children": [ 31 | { 32 | "id": "file_inclusion", 33 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L" 34 | }, 35 | { 36 | "id": "remote_code_execution_rce", 37 | "cvss_v3": "AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H" 38 | }, 39 | { 40 | "id": "sql_injection", 41 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N" 42 | } 43 | ] 44 | }, 45 | { 46 | "id": "unvalidated_redirects_and_forwards", 47 | "cvss_v3": "AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N", 48 | "children": [ 49 | { 50 | "id": "open_redirect", 51 | "children": [ 52 | { 53 | "id": "get_based", 54 | "cvss_v3": "AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N" 55 | } 56 | ] 57 | } 58 | ] 59 | }, 60 | { 61 | "id": "broken_authentication_and_session_management", 62 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 63 | }, 64 | { 65 | "id": "insecure_direct_object_references_idor", 66 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 67 | }, 68 | { 69 | "id": "sensitive_data_exposure", 70 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 71 | }, 72 | { 73 | "id": "cross_site_scripting_xss", 74 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 75 | }, 76 | { 77 | "id": "missing_function_level_access_control", 78 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 79 | }, 80 | { 81 | "id": "cross_site_request_forgery_csrf", 82 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 83 | }, 84 | { 85 | "id": "application_level_denial_of_service_dos", 86 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 87 | }, 88 | { 89 | "id": "external_behavior", 90 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 91 | }, 92 | { 93 | "id": "insufficient_security_configurability", 94 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 95 | }, 96 | { 97 | "id": "using_components_with_known_vulnerabilities", 98 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 99 | }, 100 | { 101 | "id": "insecure_data_storage", 102 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 103 | }, 104 | { 105 | "id": "lack_of_binary_hardening", 106 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 107 | }, 108 | { 109 | "id": "insecure_data_transport", 110 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 111 | }, 112 | { 113 | "id": "insecure_os_firmware", 114 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 115 | }, 116 | { 117 | "id": "broken_cryptography", 118 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 119 | }, 120 | { 121 | "id": "privacy_concerns", 122 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 123 | }, 124 | { 125 | "id": "network_security_misconfiguration", 126 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 127 | }, 128 | { 129 | "id": "mobile_security_misconfiguration", 130 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 131 | }, 132 | { 133 | "id": "client_side_injection", 134 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 135 | }, 136 | { 137 | "id": "vrt_category_only_in_test", 138 | "cvss_v3": "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N" 139 | } 140 | ] 141 | } 142 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/mappings/cwe.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": ["CWE-2000"] 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "cwe": ["CWE-933"], 9 | "children": [ 10 | { 11 | "id": "unsafe_cross_origin_resource_sharing", 12 | "cwe": ["CWE-942"] 13 | }, 14 | { 15 | "id": "path_traversal", 16 | "cwe": ["CWE-22", "CWE-73"] 17 | }, 18 | { 19 | "id": "directory_listing_enabled", 20 | "cwe": ["CWE-548"] 21 | }, 22 | { 23 | "id": "ssl_attack_breach_poodle_etc", 24 | "cwe": ["CWE-310"] 25 | }, 26 | { 27 | "id": "using_default_credentials", 28 | "cwe": ["CWE-255", "CWE-521"] 29 | }, 30 | { 31 | "id": "misconfigured_dns", 32 | "children": [ 33 | { 34 | "id": "zone_transfer", 35 | "cwe": ["CWE-669"] 36 | } 37 | ] 38 | }, 39 | { 40 | "id": "dbms_misconfiguration", 41 | "children": [ 42 | { 43 | "id": "excessively_privileged_user_dba", 44 | "cwe": ["CWE-250"] 45 | } 46 | ] 47 | }, 48 | { 49 | "id": "lack_of_password_confirmation", 50 | "children": [ 51 | { 52 | "id": "change_password", 53 | "cwe": ["CWE-620"] 54 | } 55 | ] 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/mappings/remediation_advice.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": null, 4 | "keys": ["remediation_advice", "references"] 5 | }, 6 | "content": [ 7 | { 8 | "id": "server_security_misconfiguration", 9 | "references": [ 10 | "https://www.owasp.org/index.php/Top_10_2013-A5-Security_Misconfiguration", 11 | "http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration" 12 | ], 13 | "children": [ 14 | { 15 | "id": "unsafe_cross_origin_resource_sharing", 16 | "remediation_advice": "This is advice", 17 | "references": [ 18 | "https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Cross_Origin_Resource_Sharing", 19 | "https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" 20 | ] 21 | }, 22 | { 23 | "id": "directory_listing_enabled", 24 | "remediation_advice": "Restrict directory listings being displayed from the server configuration. \n\nExample for Apache:\n\n1. Edit the server configuration file or edit/create directory .htaccess\n2. Add the following line:\nOptions -Indexes\n3. If it is the last line, make sure you have a new line after it.", 25 | "references": [ 26 | "http://projects.webappsec.org/w/page/13246922/Directory%20Indexing" 27 | ] 28 | }, 29 | { 30 | "id": "misconfigured_dns", 31 | "children": [ 32 | { 33 | "id": "subdomain_takeover", 34 | "remediation_advice": "1. Set up your external service so it fully listens to your wildcard DNS.\n2. Keep your DNS-entries constantly vetted and restricted.", 35 | "references": [ 36 | "https://labs.detectify.com/2014/10/21/hostile-subdomain-takeover-using-herokugithubdesk-more/" 37 | ] 38 | }, 39 | { 40 | "id": "zone_transfer", 41 | "remediation_advice": "Do not allow DNS zone transfers.", 42 | "references": [ 43 | "https://www.sans.org/reading-room/whitepapers/dns/securing-dns-zone-transfer-868", 44 | "https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-1999-0532" 45 | ] 46 | }, 47 | { 48 | "id": "missing_caa_record", 49 | "remediation_advice": "As the domain name holder you can modify the DNS zone file to specify one or more Certification Authorities (CAs) authorized to issue certificates for that domain.", 50 | "references": [ 51 | "https://tools.ietf.org/html/rfc6844" 52 | ] 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/mappings/test_mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "default": { 4 | "one_thing": "the thing one is" 5 | }, 6 | "keys": ["one_thing", "another_thing"] 7 | }, 8 | "content": [ 9 | { 10 | "id": "server_security_misconfiguration", 11 | "one_thing": "the thing", 12 | "another_thing": "another one" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/third-party-mappings/secure-code-warrior-links.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_security_misconfiguration": null, 3 | "server_security_misconfiguration.unsafe_cross_origin_resource_sharing": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:unsafe_cross_origin_resource_sharing&redirect=true", 4 | "server_security_misconfiguration.path_traversal": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:path_traversal&redirect=true", 5 | "server_security_misconfiguration.directory_listing_enabled": null, 6 | "server_security_misconfiguration.directory_listing_enabled.sensitive_data_exposure": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:directory_listing_enabled:sensitive_data_exposure&redirect=true", 7 | "server_security_misconfiguration.directory_listing_enabled.non_sensitive_data_exposure": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:directory_listing_enabled:non_sensitive_data_exposure&redirect=true", 8 | "server_security_misconfiguration.same_site_scripting": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:same_site_scripting&redirect=true", 9 | "server_security_misconfiguration.ssl_attack_breach_poodle_etc": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:ssl_attack_breach_poodle_etc&redirect=true", 10 | "server_security_misconfiguration.using_default_credentials": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:using_default_credentials&redirect=true", 11 | "server_security_misconfiguration.misconfigured_dns": null, 12 | "server_security_misconfiguration.misconfigured_dns.basic_subdomain_takeover": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:basic_subdomain_takeover&redirect=true", 13 | "server_security_misconfiguration.misconfigured_dns.high_impact_subdomain_takeover": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:high_impact_subdomain_takeover&redirect=true", 14 | "server_security_misconfiguration.misconfigured_dns.zone_transfer": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:zone_transfer&redirect=true", 15 | "server_security_misconfiguration.misconfigured_dns.missing_caa_record": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:misconfigured_dns:missing_caa_record&redirect=true", 16 | "server_security_misconfiguration.mail_server_misconfiguration": null, 17 | "server_security_misconfiguration.mail_server_misconfiguration.no_spoofing_protection_on_email_domain": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:no_spoofing_protection_on_email_domain&redirect=true", 18 | "server_security_misconfiguration.mail_server_misconfiguration.email_spoofing_to_inbox_due_to_missing_or_misconfigured_dmarc_on_email_domain": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:email_spoofing_to_inbox_due_to_missing_or_misconfigured_dmarc_on_email_domain&redirect=true", 19 | "server_security_misconfiguration.mail_server_misconfiguration.email_spoofing_to_spam_folder": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:email_spoofing_to_spam_folder&redirect=true", 20 | "server_security_misconfiguration.mail_server_misconfiguration.missing_or_misconfigured_spf_and_or_dkim": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:missing_or_misconfigured_spf_and_or_dkim&redirect=true", 21 | "server_security_misconfiguration.mail_server_misconfiguration.email_spoofing_on_non_email_domain": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:mail_server_misconfiguration:email_spoofing_on_non_email_domain&redirect=true", 22 | "server_security_misconfiguration.dbms_misconfiguration": null, 23 | "server_security_misconfiguration.dbms_misconfiguration.excessively_privileged_user_dba": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:dbms_misconfiguration:excessively_privileged_user_dba&redirect=true", 24 | "server_security_misconfiguration.lack_of_password_confirmation": null, 25 | "server_security_misconfiguration.lack_of_password_confirmation.change_email_address": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:change_email_address&redirect=true", 26 | "server_security_misconfiguration.lack_of_password_confirmation.change_password": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:change_password&redirect=true", 27 | "server_security_misconfiguration.lack_of_password_confirmation.delete_account": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:delete_account&redirect=true", 28 | "server_security_misconfiguration.lack_of_password_confirmation.manage_two_fa": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:lack_of_password_confirmation:manage_two_fa&redirect=true", 29 | "server_security_misconfiguration.no_rate_limiting_on_form": null, 30 | "server_security_misconfiguration.no_rate_limiting_on_form.registration": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:registration&redirect=true", 31 | "server_security_misconfiguration.no_rate_limiting_on_form.login": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:login&redirect=true", 32 | "server_security_misconfiguration.no_rate_limiting_on_form.email_triggering": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:email_triggering&redirect=true", 33 | "server_security_misconfiguration.no_rate_limiting_on_form.sms_triggering": "https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:no_rate_limiting_on_form:sms_triggering&redirect=true" 34 | } 35 | -------------------------------------------------------------------------------- /spec/sample_vrt/999.999/vulnerability-rating-taxonomy.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "release_date": "3001-02-17T00:00:00+00:00" 4 | }, 5 | "content": [ 6 | { 7 | "id": "server_security_misconfiguration", 8 | "name": "Server Security Misconfiguration (1.1)", 9 | "type": "category", 10 | "children": [ 11 | { 12 | "id": "unsafe_cross_origin_resource_sharing", 13 | "name": "Unsafe Cross-Origin Resource Sharing", 14 | "type": "subcategory", 15 | "priority": null 16 | }, 17 | { 18 | "id": "same_site_scripting", 19 | "name": "Same-Site Scripting", 20 | "type": "subcategory", 21 | "priority": 5 22 | }, 23 | { 24 | "id": "ssl_attack_breach_poodle_etc", 25 | "name": "SSL Attack (BREACH, POODLE etc.)", 26 | "type": "subcategory", 27 | "priority": null 28 | }, 29 | { 30 | "id": "using_default_credentials", 31 | "name": "Using Default Credentials", 32 | "type": "subcategory", 33 | "children": [ 34 | { 35 | "id": "production_server", 36 | "name": "Production Server", 37 | "type": "variant", 38 | "priority": 1 39 | }, 40 | { 41 | "id": "staging_development_server", 42 | "name": "Staging/Development Server", 43 | "type": "variant", 44 | "priority": 2 45 | } 46 | ] 47 | }, 48 | { 49 | "id": "misconfigured_dns", 50 | "name": "Misconfigured DNS", 51 | "type": "subcategory", 52 | "children": [ 53 | { 54 | "id": "subdomain_takeover", 55 | "name": "Subdomain Takeover", 56 | "type": "variant", 57 | "priority": 2 58 | } 59 | ] 60 | }, 61 | { 62 | "id": "mail_server_misconfiguration", 63 | "name": "Mail Server Misconfiguration", 64 | "type": "subcategory", 65 | "children": [ 66 | { 67 | "id": "missing_spf_on_email_domain", 68 | "name": "Missing SPF on Email Domain", 69 | "type": "variant", 70 | "priority": 3 71 | }, 72 | { 73 | "id": "email_spoofable_via_third_party_api_misconfiguration", 74 | "name": "Email Spoofable Via Third-Party API Misconfiguration", 75 | "type": "variant", 76 | "priority": 3 77 | }, 78 | { 79 | "id": "missing_spf_on_non_email_domain", 80 | "name": "Missing SPF on Non-Email Domain", 81 | "type": "variant", 82 | "priority": 1 83 | }, 84 | { 85 | "id": "spf_uses_a_soft_fail", 86 | "name": "SPF Uses a Soft Fail", 87 | "type": "variant", 88 | "priority": 5 89 | }, 90 | { 91 | "id": "spf_includes_10_lookups", 92 | "name": "SPF Includes > 10 Lookups", 93 | "type": "variant", 94 | "priority": 5 95 | }, 96 | { 97 | "id": "missing_dmarc", 98 | "name": "Missing DMARC", 99 | "type": "variant", 100 | "priority": 5 101 | } 102 | ] 103 | }, 104 | { 105 | "id": "lack_of_password_confirmation", 106 | "name": "Lack of Password Confirmation", 107 | "type": "subcategory", 108 | "children": [ 109 | { 110 | "id": "change_email_address", 111 | "name": "Change Email Address", 112 | "type": "variant", 113 | "priority": 4 114 | }, 115 | { 116 | "id": "change_password", 117 | "name": "Change Password", 118 | "type": "variant", 119 | "priority": 4 120 | }, 121 | { 122 | "id": "delete_account", 123 | "name": "Delete Account", 124 | "type": "variant", 125 | "priority": 4 126 | } 127 | ] 128 | }, 129 | { 130 | "id": "no_rate_limiting_on_form", 131 | "name": "No Rate Limiting on Form", 132 | "type": "subcategory", 133 | "children": [ 134 | { 135 | "id": "registration", 136 | "name": "Registration", 137 | "type": "variant", 138 | "priority": 4 139 | }, 140 | { 141 | "id": "login", 142 | "name": "Login", 143 | "type": "variant", 144 | "priority": 3 145 | }, 146 | { 147 | "id": "email_triggering", 148 | "name": "Email-Triggering", 149 | "type": "variant", 150 | "priority": 3 151 | } 152 | ] 153 | }, 154 | { 155 | "id": "unsafe_file_upload", 156 | "name": "Unsafe File Upload", 157 | "type": "subcategory", 158 | "children": [ 159 | { 160 | "id": "no_antivirus", 161 | "name": "No Antivirus", 162 | "type": "variant", 163 | "priority": 4 164 | }, 165 | { 166 | "id": "no_size_limit", 167 | "name": "No Size Limit", 168 | "type": "variant", 169 | "priority": 4 170 | }, 171 | { 172 | "id": "file_extension_filter_bypass", 173 | "name": "File Extension Filter Bypass", 174 | "type": "variant", 175 | "priority": 5 176 | } 177 | ] 178 | }, 179 | { 180 | "id": "missing_secure_or_httponly_cookie_flag", 181 | "name": "Missing Secure or HTTPOnly Cookie Flag", 182 | "type": "subcategory", 183 | "children": [ 184 | { 185 | "id": "session_token", 186 | "name": "Session Token", 187 | "type": "variant", 188 | "priority": 4 189 | }, 190 | { 191 | "id": "non_session_cookie", 192 | "name": "Non-Session Cookie", 193 | "type": "variant", 194 | "priority": 5 195 | } 196 | ] 197 | }, 198 | { 199 | "id": "clickjacking", 200 | "name": "Clickjacking", 201 | "type": "subcategory", 202 | "children": [ 203 | { 204 | "id": "sensitive_action", 205 | "name": "Sensitive Action", 206 | "type": "variant", 207 | "priority": 4 208 | }, 209 | { 210 | "id": "non_sensitive_action", 211 | "name": "Non-Sensitive Action", 212 | "type": "variant", 213 | "priority": 5 214 | } 215 | ] 216 | }, 217 | { 218 | "id": "oauth_misconfiguration", 219 | "name": "OAuth Misconfiguration", 220 | "type": "subcategory", 221 | "children": [ 222 | { 223 | "id": "missing_state_parameter", 224 | "name": "Missing State Parameter", 225 | "type": "variant", 226 | "priority": 4 227 | } 228 | ] 229 | }, 230 | { 231 | "id": "captcha_bypass", 232 | "name": "Captcha Bypass", 233 | "type": "subcategory", 234 | "children": [ 235 | { 236 | "id": "implementation_vulnerability", 237 | "name": "Implementation Vulnerability", 238 | "type": "variant", 239 | "priority": 4 240 | }, 241 | { 242 | "id": "brute_force", 243 | "name": "Brute Force", 244 | "type": "variant", 245 | "priority": 5 246 | } 247 | ] 248 | }, 249 | { 250 | "id": "exposed_admin_portal", 251 | "name": "Exposed Admin Portal", 252 | "type": "subcategory", 253 | "children": [ 254 | { 255 | "id": "to_internet", 256 | "name": "To Internet", 257 | "type": "variant", 258 | "priority": 5 259 | } 260 | ] 261 | }, 262 | { 263 | "id": "missing_dnssec", 264 | "name": "Missing DNSSEC", 265 | "type": "subcategory", 266 | "priority": 5 267 | }, 268 | { 269 | "id": "username_enumeration", 270 | "name": "Username Enumeration", 271 | "type": "subcategory", 272 | "children": [ 273 | { 274 | "id": "brute_force", 275 | "name": "Brute Force", 276 | "type": "variant", 277 | "priority": 5 278 | } 279 | ] 280 | }, 281 | { 282 | "id": "potentially_unsafe_http_method_enabled", 283 | "name": "Potentially Unsafe HTTP Method Enabled", 284 | "type": "subcategory", 285 | "children": [ 286 | { 287 | "id": "options", 288 | "name": "OPTIONS", 289 | "type": "variant", 290 | "priority": 5 291 | }, 292 | { 293 | "id": "trace", 294 | "name": "TRACE", 295 | "type": "variant", 296 | "priority": 5 297 | } 298 | ] 299 | }, 300 | { 301 | "id": "insecure_ssl", 302 | "name": "Insecure SSL", 303 | "type": "subcategory", 304 | "children": [ 305 | { 306 | "id": "lack_of_forward_secrecy", 307 | "name": "Lack of Forward Secrecy", 308 | "type": "variant", 309 | "priority": 5 310 | }, 311 | { 312 | "id": "insecure_cipher_suite", 313 | "name": "Insecure Cipher Suite", 314 | "type": "variant", 315 | "priority": 5 316 | } 317 | ] 318 | }, 319 | { 320 | "id": "lack_of_security_headers", 321 | "name": "Lack of Security Headers", 322 | "type": "subcategory", 323 | "children": [ 324 | { 325 | "id": "x_frame_options", 326 | "name": "X-Frame-Options", 327 | "type": "variant", 328 | "priority": 5 329 | }, 330 | { 331 | "id": "cache_control_for_a_non_sensitive_page", 332 | "name": "Cache-Control for a Non-Sensitive Page", 333 | "type": "variant", 334 | "priority": 5 335 | }, 336 | { 337 | "id": "x_xss_protection", 338 | "name": "X-XSS-Protection", 339 | "type": "variant", 340 | "priority": 5 341 | }, 342 | { 343 | "id": "strict_transport_security", 344 | "name": "Strict-Transport-Security", 345 | "type": "variant", 346 | "priority": 5 347 | }, 348 | { 349 | "id": "x_content_type_options", 350 | "name": "X-Content-Type-Options", 351 | "type": "variant", 352 | "priority": 5 353 | }, 354 | { 355 | "id": "content_security_policy", 356 | "name": "Content-Security-Policy", 357 | "type": "variant", 358 | "priority": 5 359 | }, 360 | { 361 | "id": "public_key_pins", 362 | "name": "Public-Key-Pins", 363 | "type": "variant", 364 | "priority": 5 365 | }, 366 | { 367 | "id": "x_content_security_policy", 368 | "name": "X-Content-Security-Policy", 369 | "type": "variant", 370 | "priority": 5 371 | }, 372 | { 373 | "id": "x_webkit_csp", 374 | "name": "X-Webkit-CSP", 375 | "type": "variant", 376 | "priority": 5 377 | }, 378 | { 379 | "id": "content_security_policy_report_only", 380 | "name": "Content-Security-Policy-Report-Only", 381 | "type": "variant", 382 | "priority": 5 383 | }, 384 | { 385 | "id": "cache_control_for_a_sensitive_page", 386 | "name": "Cache-Control for a Sensitive Page", 387 | "type": "variant", 388 | "priority": 4 389 | } 390 | ] 391 | } 392 | ] 393 | }, 394 | { 395 | "id": "2fa_example", 396 | "name": "2FA Example Vuln", 397 | "type": "category", 398 | "priority": 4, 399 | "children": [] 400 | }, 401 | { 402 | "id": "f", 403 | "name": "Short id example", 404 | "type": "category", 405 | "priority": null 406 | }, 407 | { 408 | "id": "server_side_injection", 409 | "name": "Server-Side Injection", 410 | "type": "category", 411 | "children": [ 412 | { 413 | "id": "file_inclusion", 414 | "name": "File Inclusion", 415 | "type": "subcategory", 416 | "children": [ 417 | { 418 | "id": "local", 419 | "name": "Local", 420 | "type": "variant", 421 | "priority": 1 422 | } 423 | ] 424 | }, 425 | { 426 | "id": "parameter_pollution", 427 | "name": "Parameter Pollution", 428 | "type": "subcategory", 429 | "children": [ 430 | { 431 | "id": "social_media_sharing_buttons", 432 | "name": "Social Media Sharing Buttons", 433 | "type": "variant", 434 | "priority": 5 435 | } 436 | ] 437 | }, 438 | { 439 | "id": "remote_code_execution_rce", 440 | "name": "Remote Code Execution (RCE)", 441 | "type": "subcategory", 442 | "priority": 1 443 | }, 444 | { 445 | "id": "sql_injection", 446 | "name": "SQL Injection", 447 | "type": "subcategory", 448 | "children": [ 449 | { 450 | "id": "error_based", 451 | "name": "Error-Based", 452 | "type": "variant", 453 | "priority": 1 454 | }, 455 | { 456 | "id": "blind", 457 | "name": "Blind", 458 | "type": "variant", 459 | "priority": 1 460 | } 461 | ] 462 | }, 463 | { 464 | "id": "xml_external_entity_injection_xxe", 465 | "name": "XML External Entity Injection (XXE)", 466 | "type": "subcategory", 467 | "priority": 1 468 | }, 469 | { 470 | "id": "http_response_manipulation", 471 | "name": "HTTP Response Manipulation", 472 | "type": "subcategory", 473 | "children": [ 474 | { 475 | "id": "response_splitting_crlf", 476 | "name": "Response Splitting (CRLF)", 477 | "type": "variant", 478 | "priority": 3 479 | } 480 | ] 481 | }, 482 | { 483 | "id": "content_spoofing", 484 | "name": "Content Spoofing", 485 | "type": "subcategory", 486 | "children": [ 487 | { 488 | "id": "iframe_injection", 489 | "name": "iframe Injection", 490 | "type": "variant", 491 | "priority": 3 492 | }, 493 | { 494 | "id": "external_authentication_injection", 495 | "name": "External Authentication Injection", 496 | "type": "variant", 497 | "priority": 4 498 | }, 499 | { 500 | "id": "email_html_injection", 501 | "name": "Email HTML Injection", 502 | "type": "variant", 503 | "priority": 4 504 | }, 505 | { 506 | "id": "text_injection", 507 | "name": "Text Injection", 508 | "type": "variant", 509 | "priority": 5 510 | }, 511 | { 512 | "id": "homograph_idn_based", 513 | "name": "Homograph/IDN-Based", 514 | "type": "variant", 515 | "priority": 5 516 | } 517 | ] 518 | } 519 | ] 520 | }, 521 | { 522 | "id": "broken_authentication_and_session_management", 523 | "name": "Broken Authentication and Session Management", 524 | "type": "category", 525 | "children": [ 526 | { 527 | "id": "authentication_bypass", 528 | "name": "Authentication Bypass", 529 | "type": "subcategory", 530 | "children": [ 531 | { 532 | "id": "vertical", 533 | "name": "Vertical", 534 | "type": "variant", 535 | "priority": 1 536 | }, 537 | { 538 | "id": "horizontal", 539 | "name": "Horizontal", 540 | "type": "variant", 541 | "priority": 2 542 | } 543 | ] 544 | }, 545 | { 546 | "id": "weak_login_function", 547 | "name": "Weak Login Function", 548 | "type": "subcategory", 549 | "children": [ 550 | { 551 | "id": "over_http", 552 | "name": "Over HTTP", 553 | "type": "variant", 554 | "priority": 3 555 | } 556 | ] 557 | }, 558 | { 559 | "id": "session_fixation", 560 | "name": "Session Fixation", 561 | "type": "subcategory", 562 | "priority": 3 563 | }, 564 | { 565 | "id": "failure_to_invalidate_session", 566 | "name": "Failure to Invalidate Session", 567 | "type": "subcategory", 568 | "children": [ 569 | { 570 | "id": "on_logout", 571 | "name": "On Logout", 572 | "type": "variant", 573 | "priority": 4 574 | }, 575 | { 576 | "id": "on_password_reset", 577 | "name": "On Password Reset", 578 | "type": "variant", 579 | "priority": 4 580 | }, 581 | { 582 | "id": "on_password_change", 583 | "name": "On Password Change", 584 | "type": "variant", 585 | "priority": 4 586 | }, 587 | { 588 | "id": "all_sessions", 589 | "name": "All Sessions", 590 | "type": "variant", 591 | "priority": 5 592 | }, 593 | { 594 | "id": "on_email_change", 595 | "name": "On Email Change", 596 | "type": "variant", 597 | "priority": 5 598 | }, 599 | { 600 | "id": "long_timeout", 601 | "name": "Long Timeout", 602 | "type": "variant", 603 | "priority": 5 604 | } 605 | ] 606 | }, 607 | { 608 | "id": "session_token_in_url", 609 | "name": "Session Token in URL", 610 | "type": "subcategory", 611 | "children": [ 612 | { 613 | "id": "over_http", 614 | "name": "Over HTTP", 615 | "type": "variant", 616 | "priority": 4 617 | }, 618 | { 619 | "id": "over_https", 620 | "name": "Over HTTPS", 621 | "type": "variant", 622 | "priority": 5 623 | } 624 | ] 625 | }, 626 | { 627 | "id": "concurrent_logins", 628 | "name": "Concurrent Logins", 629 | "type": "subcategory", 630 | "priority": 5 631 | }, 632 | { 633 | "id": "weak_registration_implementation", 634 | "name": "Weak Registration Implementation", 635 | "type": "subcategory", 636 | "children": [ 637 | { 638 | "id": "over_http", 639 | "name": "Over HTTP", 640 | "type": "variant", 641 | "priority": 4 642 | } 643 | ] 644 | } 645 | ] 646 | }, 647 | { 648 | "id": "insecure_direct_object_references_idor", 649 | "name": "Insecure Direct Object References (IDOR)", 650 | "type": "category", 651 | "priority": null 652 | }, 653 | { 654 | "id": "sensitive_data_exposure", 655 | "name": "Sensitive Data Exposure", 656 | "type": "category", 657 | "children": [ 658 | { 659 | "id": "critically_sensitive_data", 660 | "name": "Critically Sensitive Data", 661 | "type": "subcategory", 662 | "children": [ 663 | { 664 | "id": "password_disclosure", 665 | "name": "Password Disclosure", 666 | "type": "variant", 667 | "priority": 1 668 | }, 669 | { 670 | "id": "private_api_keys", 671 | "name": "Private API Keys", 672 | "type": "variant", 673 | "priority": 1 674 | } 675 | ] 676 | }, 677 | { 678 | "id": "exif_geolocation_data_not_stripped_from_uploaded_images", 679 | "name": "EXIF Geolocation Data Not Stripped From Uploaded Images", 680 | "type": "subcategory", 681 | "children": [ 682 | { 683 | "id": "automatic_user_enumeration", 684 | "name": "Automatic User Enumeration", 685 | "type": "variant", 686 | "priority": 3 687 | }, 688 | { 689 | "id": "manual_user_enumeration", 690 | "name": "Manual User Enumeration", 691 | "type": "variant", 692 | "priority": 4 693 | } 694 | ] 695 | }, 696 | { 697 | "id": "visible_detailed_error_page", 698 | "name": "Visible Detailed Error Page", 699 | "type": "subcategory", 700 | "priority": null 701 | }, 702 | { 703 | "id": "disclosure_of_known_public_information", 704 | "name": "Disclosure of Known Public Information", 705 | "type": "subcategory", 706 | "priority": 5 707 | }, 708 | { 709 | "id": "token_leakage_via_referer", 710 | "name": "Token Leakage via Referer", 711 | "type": "subcategory", 712 | "children": [ 713 | { 714 | "id": "trusted_3rd_party", 715 | "name": "Trusted 3rd Party", 716 | "type": "variant", 717 | "priority": 5 718 | }, 719 | { 720 | "id": "untrusted_3rd_party", 721 | "name": "Untrusted 3rd Party", 722 | "type": "variant", 723 | "priority": 4 724 | }, 725 | { 726 | "id": "over_http", 727 | "name": "Over HTTP", 728 | "type": "variant", 729 | "priority": 4 730 | } 731 | ] 732 | }, 733 | { 734 | "id": "sensitive_token_in_url", 735 | "name": "Sensitive Token in URL", 736 | "type": "subcategory", 737 | "priority": 4 738 | }, 739 | { 740 | "id": "weak_password_reset_implementation", 741 | "name": "Weak Password Reset Implementation", 742 | "type": "subcategory", 743 | "children": [ 744 | { 745 | "id": "password_reset_token_sent_over_http", 746 | "name": "Password Reset Token Sent Over HTTP", 747 | "type": "variant", 748 | "priority": 4 749 | } 750 | ] 751 | }, 752 | { 753 | "id": "mixed_content", 754 | "name": "Mixed Content", 755 | "type": "subcategory", 756 | "children": [ 757 | { 758 | "id": "sensitive_data_disclosure", 759 | "name": "Sensitive Data Disclosure", 760 | "type": "variant", 761 | "priority": 4 762 | }, 763 | { 764 | "id": "requires_being_a_man_in_the_middle", 765 | "name": "Requires Being a Man-in-the-Middle", 766 | "type": "variant", 767 | "priority": 5 768 | } 769 | ] 770 | }, 771 | { 772 | "id": "sensitive_data_hardcoded", 773 | "name": "Sensitive Data Hardcoded", 774 | "type": "subcategory", 775 | "children": [ 776 | { 777 | "id": "oauth_secret", 778 | "name": "OAuth Secret", 779 | "type": "variant", 780 | "priority": 5 781 | }, 782 | { 783 | "id": "file_paths", 784 | "name": "File Paths", 785 | "type": "variant", 786 | "priority": 5 787 | } 788 | ] 789 | }, 790 | { 791 | "id": "non_sensitive_token_in_url", 792 | "name": "Non-Sensitive Token in URL", 793 | "type": "subcategory", 794 | "priority": 5 795 | } 796 | ] 797 | }, 798 | { 799 | "id": "cross_site_scripting_xss", 800 | "name": "Cross-Site Scripting (XSS)", 801 | "type": "category", 802 | "children": [ 803 | { 804 | "id": "stored", 805 | "name": "Stored", 806 | "type": "subcategory", 807 | "children": [ 808 | { 809 | "id": "non_admin_to_anyone", 810 | "name": "Non-Admin to Anyone", 811 | "type": "variant", 812 | "priority": 2 813 | }, 814 | { 815 | "id": "admin_to_anyone", 816 | "name": "Admin to Anyone", 817 | "type": "variant", 818 | "priority": 3 819 | }, 820 | { 821 | "id": "self", 822 | "name": "Self", 823 | "type": "variant", 824 | "priority": 5 825 | } 826 | ] 827 | }, 828 | { 829 | "id": "reflected", 830 | "name": "Reflected", 831 | "type": "subcategory", 832 | "children": [ 833 | { 834 | "id": "non_admin_to_anyone", 835 | "name": "Non-Admin to Anyone", 836 | "type": "variant", 837 | "priority": 3 838 | }, 839 | { 840 | "id": "admin_to_anyone", 841 | "name": "Admin to Anyone", 842 | "type": "variant", 843 | "priority": 4 844 | }, 845 | { 846 | "id": "self", 847 | "name": "Self", 848 | "type": "variant", 849 | "priority": 5 850 | } 851 | ] 852 | }, 853 | { 854 | "id": "cookie_based", 855 | "name": "Cookie-Based", 856 | "type": "subcategory", 857 | "priority": 4 858 | }, 859 | { 860 | "id": "ie_only", 861 | "name": "IE-Only", 862 | "type": "subcategory", 863 | "children": [ 864 | { 865 | "id": "older_version_ie_10_11", 866 | "name": "Older Version (IE 10/11)", 867 | "type": "variant", 868 | "priority": 4 869 | }, 870 | { 871 | "id": "xss_filter_disabled", 872 | "name": "XSS Filter Disabled", 873 | "type": "variant", 874 | "priority": 5 875 | }, 876 | { 877 | "id": "older_version_ie10", 878 | "name": "Older Version (< IE10)", 879 | "type": "variant", 880 | "priority": 5 881 | } 882 | ] 883 | }, 884 | { 885 | "id": "referer", 886 | "name": "Referer", 887 | "type": "subcategory", 888 | "priority": 4 889 | }, 890 | { 891 | "id": "trace_method", 892 | "name": "TRACE Method", 893 | "type": "subcategory", 894 | "priority": 5 895 | }, 896 | { 897 | "id": "universal_uxss", 898 | "name": "Universal (UXSS)", 899 | "type": "subcategory", 900 | "priority": 4 901 | }, 902 | { 903 | "id": "off_domain", 904 | "name": "Off-Domain", 905 | "type": "subcategory", 906 | "children": [ 907 | { 908 | "id": "data_uri", 909 | "name": "Data URI", 910 | "type": "variant", 911 | "priority": 4 912 | } 913 | ] 914 | } 915 | ] 916 | }, 917 | { 918 | "id": "missing_function_level_access_control", 919 | "name": "Missing Function Level Access Control", 920 | "type": "category", 921 | "children": [ 922 | { 923 | "id": "server_side_request_forgery_ssrf", 924 | "name": "Server-Side Request Forgery (SSRF)", 925 | "type": "subcategory", 926 | "children": [ 927 | { 928 | "id": "internal", 929 | "name": "Internal", 930 | "type": "variant", 931 | "priority": 2 932 | }, 933 | { 934 | "id": "external", 935 | "name": "External", 936 | "type": "variant", 937 | "priority": 4 938 | } 939 | ] 940 | }, 941 | { 942 | "id": "username_enumeration", 943 | "name": "Username Enumeration", 944 | "type": "subcategory", 945 | "children": [ 946 | { 947 | "id": "data_leak", 948 | "name": "Data Leak", 949 | "type": "variant", 950 | "priority": 4 951 | } 952 | ] 953 | }, 954 | { 955 | "id": "exposed_sensitive_android_intent", 956 | "name": "Exposed Sensitive Android Intent", 957 | "type": "subcategory", 958 | "priority": null 959 | }, 960 | { 961 | "id": "exposed_sensitive_ios_url_scheme", 962 | "name": "Exposed Sensitive iOS URL Scheme", 963 | "type": "subcategory", 964 | "priority": null 965 | } 966 | ] 967 | }, 968 | { 969 | "id": "cross_site_request_forgery_csrf", 970 | "name": "Cross-Site Request Forgery (CSRF)", 971 | "type": "category", 972 | "priority": null 973 | }, 974 | { 975 | "id": "application_level_denial_of_service_dos", 976 | "name": "Application-Level Denial-of-Service (DoS)", 977 | "type": "category", 978 | "children": [ 979 | { 980 | "id": "critical_impact_and_or_easy_difficulty", 981 | "name": "Critical Impact and/or Easy Difficulty", 982 | "type": "subcategory", 983 | "priority": 2 984 | }, 985 | { 986 | "id": "high_impact_and_or_medium_difficulty", 987 | "name": "High Impact and/or Medium Difficulty", 988 | "type": "subcategory", 989 | "priority": 3 990 | }, 991 | { 992 | "id": "app_crash", 993 | "name": "App Crash", 994 | "type": "subcategory", 995 | "children": [ 996 | { 997 | "id": "malformed_android_intents", 998 | "name": "Malformed Android Intents", 999 | "type": "variant", 1000 | "priority": 5 1001 | }, 1002 | { 1003 | "id": "malformed_ios_url_schemes", 1004 | "name": "Malformed iOS URL Schemes", 1005 | "type": "variant", 1006 | "priority": 5 1007 | } 1008 | ] 1009 | } 1010 | ] 1011 | }, 1012 | { 1013 | "id": "unvalidated_redirects_and_forwards", 1014 | "name": "Unvalidated Redirects and Forwards", 1015 | "type": "category", 1016 | "children": [ 1017 | { 1018 | "id": "open_redirect", 1019 | "name": "Open Redirect", 1020 | "type": "subcategory", 1021 | "children": [ 1022 | { 1023 | "id": "get_based", 1024 | "name": "GET-Based", 1025 | "type": "variant", 1026 | "priority": 4 1027 | } 1028 | ] 1029 | }, 1030 | { 1031 | "id": "tabnabbing", 1032 | "name": "Tabnabbing", 1033 | "type": "subcategory", 1034 | "priority": 5 1035 | } 1036 | ] 1037 | }, 1038 | { 1039 | "id": "external_behavior", 1040 | "name": "External Behavior", 1041 | "type": "category", 1042 | "children": [ 1043 | { 1044 | "id": "lack_of_security_speed_bump_page", 1045 | "name": "Lack of Security Speed Bump Page", 1046 | "type": "subcategory", 1047 | "priority": 5 1048 | }, 1049 | { 1050 | "id": "browser_feature", 1051 | "name": "Browser Feature", 1052 | "type": "subcategory", 1053 | "children": [ 1054 | { 1055 | "id": "plaintext_password_field", 1056 | "name": "Plaintext Password Field", 1057 | "type": "variant", 1058 | "priority": 5 1059 | }, 1060 | { 1061 | "id": "save_password", 1062 | "name": "Save Password", 1063 | "type": "variant", 1064 | "priority": 5 1065 | }, 1066 | { 1067 | "id": "autocomplete_enabled", 1068 | "name": "Autocomplete Enabled", 1069 | "type": "variant", 1070 | "priority": 5 1071 | }, 1072 | { 1073 | "id": "autocorrect_enabled", 1074 | "name": "Autocorrect Enabled", 1075 | "type": "variant", 1076 | "priority": 5 1077 | }, 1078 | { 1079 | "id": "aggressive_offline_caching", 1080 | "name": "Aggressive Offline Caching", 1081 | "type": "variant", 1082 | "priority": 5 1083 | } 1084 | ] 1085 | }, 1086 | { 1087 | "id": "csv_injection", 1088 | "name": "CSV Injection", 1089 | "type": "subcategory", 1090 | "priority": 5 1091 | }, 1092 | { 1093 | "id": "captcha_bypass", 1094 | "name": "Captcha Bypass", 1095 | "type": "subcategory", 1096 | "children": [ 1097 | { 1098 | "id": "crowdsourcing", 1099 | "name": "Crowdsourcing", 1100 | "type": "variant", 1101 | "priority": 5 1102 | } 1103 | ] 1104 | }, 1105 | { 1106 | "id": "system_clipboard_leak", 1107 | "name": "System Clipboard Leak", 1108 | "type": "subcategory", 1109 | "children": [ 1110 | { 1111 | "id": "shared_links", 1112 | "name": "Shared Links", 1113 | "type": "variant", 1114 | "priority": 5 1115 | } 1116 | ] 1117 | }, 1118 | { 1119 | "id": "user_password_persisted_in_memory", 1120 | "name": "User Password Persisted in Memory", 1121 | "type": "subcategory", 1122 | "priority": 5 1123 | } 1124 | ] 1125 | }, 1126 | { 1127 | "id": "insufficient_security_configurability", 1128 | "name": "Insufficient Security Configurability", 1129 | "type": "category", 1130 | "children": [ 1131 | { 1132 | "id": "weak_password_policy", 1133 | "name": "Weak Password Policy", 1134 | "type": "subcategory", 1135 | "children": [ 1136 | { 1137 | "id": "complexity_both_length_and_char_type_not_enforced", 1138 | "name": "Complexity, Both Length and Char Type Not Enforced", 1139 | "type": "variant", 1140 | "priority": 3 1141 | }, 1142 | { 1143 | "id": "complexity_length_not_enforced", 1144 | "name": "Complexity, Length Not Enforced", 1145 | "type": "variant", 1146 | "priority": 4 1147 | }, 1148 | { 1149 | "id": "complexity_char_type_not_enforced", 1150 | "name": "Complexity, Char Type Not Enforced", 1151 | "type": "variant", 1152 | "priority": 4 1153 | }, 1154 | { 1155 | "id": "allows_reuse_of_old_passwords", 1156 | "name": "Allows Reuse of Old Passwords", 1157 | "type": "variant", 1158 | "priority": 5 1159 | }, 1160 | { 1161 | "id": "allows_password_to_be_same_as_email_username", 1162 | "name": "Allows Password to be Same as Email/Username", 1163 | "type": "variant", 1164 | "priority": 5 1165 | } 1166 | ] 1167 | }, 1168 | { 1169 | "id": "weak_password_reset_implementation", 1170 | "name": "Weak Password Reset Implementation", 1171 | "type": "subcategory", 1172 | "children": [ 1173 | { 1174 | "id": "token_is_not_invalidated_after_use", 1175 | "name": "Token is Not Invalidated After Use", 1176 | "type": "variant", 1177 | "priority": 4 1178 | }, 1179 | { 1180 | "id": "token_is_not_invalidated_after_email_change", 1181 | "name": "Token is Not Invalidated After Email Change", 1182 | "type": "variant", 1183 | "priority": 5 1184 | }, 1185 | { 1186 | "id": "token_is_not_invalidated_after_password_change", 1187 | "name": "Token is Not Invalidated After Password Change", 1188 | "type": "variant", 1189 | "priority": 5 1190 | }, 1191 | { 1192 | "id": "token_has_long_timed_expiry", 1193 | "name": "Token Has Long Timed Expiry", 1194 | "type": "variant", 1195 | "priority": 5 1196 | }, 1197 | { 1198 | "id": "token_is_not_invalidated_after_new_token_is_requested", 1199 | "name": "Token is Not Invalidated After New Token is Requested", 1200 | "type": "variant", 1201 | "priority": 5 1202 | } 1203 | ] 1204 | }, 1205 | { 1206 | "id": "lack_of_verification_email", 1207 | "name": "Lack of Verification Email", 1208 | "type": "subcategory", 1209 | "priority": 5 1210 | }, 1211 | { 1212 | "id": "lack_of_notification_email", 1213 | "name": "Lack of Notification Email", 1214 | "type": "subcategory", 1215 | "priority": 5 1216 | }, 1217 | { 1218 | "id": "weak_registration_implementation", 1219 | "name": "Weak Registration Implementation", 1220 | "type": "subcategory", 1221 | "children": [ 1222 | { 1223 | "id": "allows_disposable_email_addresses", 1224 | "name": "Allows Disposable Email Addresses", 1225 | "type": "variant", 1226 | "priority": 5 1227 | } 1228 | ] 1229 | }, 1230 | { 1231 | "id": "weak_2fa_implementation", 1232 | "name": "Weak 2FA Implementation", 1233 | "type": "subcategory", 1234 | "children": [ 1235 | { 1236 | "id": "missing_failsafe", 1237 | "name": "Missing Failsafe", 1238 | "type": "variant", 1239 | "priority": 5 1240 | } 1241 | ] 1242 | } 1243 | ] 1244 | }, 1245 | { 1246 | "id": "using_components_with_known_vulnerabilities", 1247 | "name": "Using Components with Known Vulnerabilities", 1248 | "type": "category", 1249 | "children": [ 1250 | { 1251 | "id": "rosetta_flash", 1252 | "name": "Rosetta Flash", 1253 | "type": "subcategory", 1254 | "priority": 4 1255 | }, 1256 | { 1257 | "id": "outdated_software_version", 1258 | "name": "Outdated Software Version", 1259 | "type": "subcategory", 1260 | "priority": 5 1261 | }, 1262 | { 1263 | "id": "captcha_bypass", 1264 | "name": "Captcha Bypass", 1265 | "type": "subcategory", 1266 | "children": [ 1267 | { 1268 | "id": "ocr_optical_character_recognition", 1269 | "name": "OCR (Optical Character Recognition)", 1270 | "type": "variant", 1271 | "priority": 5 1272 | } 1273 | ] 1274 | } 1275 | ] 1276 | }, 1277 | { 1278 | "id": "insecure_data_storage", 1279 | "name": "Insecure Data Storage", 1280 | "type": "category", 1281 | "children": [ 1282 | { 1283 | "id": "credentials_stored_unencrypted", 1284 | "name": "Credentials Stored Unencrypted", 1285 | "type": "subcategory", 1286 | "children": [ 1287 | { 1288 | "id": "on_external_storage", 1289 | "name": "On External Storage", 1290 | "type": "variant", 1291 | "priority": 4 1292 | }, 1293 | { 1294 | "id": "on_internal_storage", 1295 | "name": "On Internal Storage", 1296 | "type": "variant", 1297 | "priority": 5 1298 | } 1299 | ] 1300 | }, 1301 | { 1302 | "id": "sensitive_application_data_stored_unencrypted", 1303 | "name": "Sensitive Application Data Stored Unencrypted", 1304 | "type": "subcategory", 1305 | "children": [ 1306 | { 1307 | "id": "on_external_storage", 1308 | "name": "On External Storage", 1309 | "type": "variant", 1310 | "priority": 4 1311 | }, 1312 | { 1313 | "id": "on_internal_storage", 1314 | "name": "On Internal Storage", 1315 | "type": "variant", 1316 | "priority": 5 1317 | } 1318 | ] 1319 | }, 1320 | { 1321 | "id": "non_sensitive_application_data_stored_unencrypted", 1322 | "name": "Non-Sensitive Application Data Stored Unencrypted", 1323 | "type": "subcategory", 1324 | "priority": 5 1325 | }, 1326 | { 1327 | "id": "screen_caching_enabled", 1328 | "name": "Screen Caching Enabled", 1329 | "type": "subcategory", 1330 | "priority": 5 1331 | } 1332 | ] 1333 | }, 1334 | { 1335 | "id": "lack_of_binary_hardening", 1336 | "name": "Lack of Binary Hardening", 1337 | "type": "category", 1338 | "children": [ 1339 | { 1340 | "id": "lack_of_exploit_mitigations", 1341 | "name": "Lack of Exploit Mitigations", 1342 | "type": "subcategory", 1343 | "priority": 5 1344 | }, 1345 | { 1346 | "id": "lack_of_jailbreak_detection", 1347 | "name": "Lack of Jailbreak Detection", 1348 | "type": "subcategory", 1349 | "priority": 5 1350 | }, 1351 | { 1352 | "id": "lack_of_obfuscation", 1353 | "name": "Lack of Obfuscation", 1354 | "type": "subcategory", 1355 | "priority": 5 1356 | }, 1357 | { 1358 | "id": "runtime_instrumentation_based", 1359 | "name": "Runtime Instrumentation-Based", 1360 | "type": "subcategory", 1361 | "priority": 5 1362 | } 1363 | ] 1364 | }, 1365 | { 1366 | "id": "insecure_data_transport", 1367 | "name": "Insecure Data Transport", 1368 | "type": "category", 1369 | "children": [ 1370 | { 1371 | "id": "ssl_certificate_pinning", 1372 | "name": "SSL Certificate Pinning", 1373 | "type": "subcategory", 1374 | "children": [ 1375 | { 1376 | "id": "absent", 1377 | "name": "Absent", 1378 | "type": "variant", 1379 | "priority": 5 1380 | }, 1381 | { 1382 | "id": "defeatable", 1383 | "name": "Defeatable", 1384 | "type": "variant", 1385 | "priority": 5 1386 | } 1387 | ] 1388 | } 1389 | ] 1390 | }, 1391 | { 1392 | "id": "insecure_os_firmware", 1393 | "name": "Insecure OS/Firmware", 1394 | "type": "category", 1395 | "children": [ 1396 | { 1397 | "id": "hardcoded_password", 1398 | "name": "Hardcoded Password", 1399 | "type": "subcategory", 1400 | "children": [ 1401 | { 1402 | "id": "privileged_user", 1403 | "name": "Privileged User", 1404 | "type": "variant", 1405 | "priority": 1 1406 | }, 1407 | { 1408 | "id": "non_privileged_user", 1409 | "name": "Non-Privileged User", 1410 | "type": "variant", 1411 | "priority": 2 1412 | } 1413 | ] 1414 | } 1415 | ] 1416 | }, 1417 | { 1418 | "id": "broken_cryptography", 1419 | "name": "Broken Cryptography", 1420 | "type": "category", 1421 | "children": [ 1422 | { 1423 | "id": "command_injection", 1424 | "name": "Command Injection", 1425 | "type": "subcategory", 1426 | "priority": 1 1427 | }, 1428 | { 1429 | "id": "cryptographic_flaw", 1430 | "name": "Cryptographic Flaw", 1431 | "type": "subcategory", 1432 | "children": [ 1433 | { 1434 | "id": "incorrect_usage", 1435 | "name": "Incorrect Usage", 1436 | "type": "variant", 1437 | "priority": 1 1438 | } 1439 | ] 1440 | } 1441 | ] 1442 | }, 1443 | { 1444 | "id": "privacy_concerns", 1445 | "name": "Privacy Concerns", 1446 | "type": "category", 1447 | "children": [ 1448 | { 1449 | "id": "unnecessary_data_collection", 1450 | "name": "Unnecessary Data Collection", 1451 | "type": "subcategory", 1452 | "children": [ 1453 | { 1454 | "id": "wifi_ssid_password", 1455 | "name": "WiFi SSID+Password", 1456 | "type": "variant", 1457 | "priority": 4 1458 | } 1459 | ] 1460 | } 1461 | ] 1462 | }, 1463 | { 1464 | "id": "network_security_misconfiguration", 1465 | "name": "Network Security Misconfiguration", 1466 | "type": "category", 1467 | "children": [ 1468 | { 1469 | "id": "telnet_enabled", 1470 | "name": "Telnet Enabled", 1471 | "type": "subcategory", 1472 | "children": [ 1473 | { 1474 | "id": "credentials_required", 1475 | "name": "Credentials Required", 1476 | "type": "variant", 1477 | "priority": 4 1478 | } 1479 | ] 1480 | } 1481 | ] 1482 | }, 1483 | { 1484 | "id": "mobile_security_misconfiguration", 1485 | "name": "Mobile Security Misconfiguration", 1486 | "type": "category", 1487 | "priority": null 1488 | }, 1489 | { 1490 | "id": "client_side_injection", 1491 | "name": "Client-Side Injection", 1492 | "type": "category", 1493 | "priority": null 1494 | }, 1495 | { 1496 | "id": "vrt_category_only_in_test", 1497 | "name": "Poor Test Data Stinks", 1498 | "type": "category", 1499 | "priority": null 1500 | } 1501 | ] 1502 | } 1503 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'vrt' 3 | 4 | RSpec.configure do |config| 5 | config.expect_with :rspec do |c| 6 | c.syntax = :expect 7 | end 8 | 9 | config.before(:each) do 10 | # The following adds a new, test-only VRT version ('999.999') 11 | VRT.unload! 12 | stub_const('VRT::DIR', Pathname.new('spec/sample_vrt')) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/vrt/map_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VRT::Map do 4 | let(:vrt) { VRT } 5 | let(:dir) { vrt::DIR } 6 | let(:sample_map) { described_class.new('999.999') } 7 | 8 | before do 9 | VRT.unload! 10 | allow(vrt).to receive(:current_version).and_return('2.0') 11 | end 12 | 13 | after { VRT.unload! } 14 | 15 | context 'with no argument' do 16 | let(:current_map) { described_class.new } 17 | 18 | it 'creates a VRT Map using the current version' do 19 | expect(current_map.version).to eq('2.0') 20 | end 21 | end 22 | 23 | context 'with an argument' do 24 | it 'creates a VRT Map using a specific version' do 25 | expect(sample_map.version).to eq('999.999') 26 | end 27 | end 28 | 29 | describe '#build_structure' do 30 | it 'builds a valid hash for the VRT structure on instantiation' do 31 | expect(sample_map.structure).to include(:server_side_injection) 32 | expect(sample_map.structure[:server_side_injection]).to be_a(VRT::Node) 33 | end 34 | end 35 | 36 | context 'when the structure is queried' do 37 | let(:good_query) { 'server_side_injection.sql_injection.blind' } 38 | let(:sneaky_query) { 'server_side_injection.sql_injection.blind.sneaky_guy' } 39 | let(:bad_query) { 'ssl_attack_breach_poodle_etc.unsafe_cross_origin_resource_sharing.critical_impact' } 40 | 41 | it 'finds a category when given a query string' do 42 | node = sample_map.find_node(good_query) 43 | expect(node).to be_a(VRT::Node) 44 | expect(node.id).to eq(:blind) 45 | end 46 | 47 | it 'can determine the validity of a given query string' do 48 | expect(sample_map.valid?(good_query)).to be_truthy 49 | expect(sample_map.valid?(sneaky_query)).to be_falsey 50 | expect(sample_map.valid?(bad_query)).to be_falsey 51 | end 52 | end 53 | 54 | describe '#get_lineage' do 55 | context 'with a complex hierarchy' do 56 | let(:id) { 'server_security_misconfiguration.using_default_credentials.production_server' } 57 | 58 | it 'returns a formatted lineage string' do 59 | expect(sample_map.get_lineage(id)).to eq( 60 | 'Server Security Misconfiguration (1.1) > Using Default Credentials > Production Server' 61 | ) 62 | end 63 | 64 | context 'when specifying max depth' do 65 | it 'returns a formatted lineage string with the correct max depth' do 66 | expect(sample_map.get_lineage(id, max_depth: 'subcategory')).to eq( 67 | 'Server Security Misconfiguration (1.1) > Using Default Credentials' 68 | ) 69 | end 70 | 71 | it 'returns a formatted lineage string with the correct max depth' do 72 | expect(sample_map.get_lineage(id, max_depth: 'category')).to eq( 73 | 'Server Security Misconfiguration (1.1)' 74 | ) 75 | end 76 | end 77 | end 78 | 79 | context 'with a single-level element' do 80 | let(:id) { 'insecure_direct_object_references_idor' } 81 | 82 | it 'returns a formatted lineage string' do 83 | expect(sample_map.get_lineage(id)).to eq 'Insecure Direct Object References (IDOR)' 84 | end 85 | end 86 | end 87 | 88 | describe '#valid?' do 89 | subject { sample_map.valid?(string) } 90 | 91 | context 'when uppercase characters' do 92 | let(:string) { 'server_Security_misconfiguration' } 93 | 94 | it { is_expected.to be_falsey } 95 | end 96 | 97 | context 'with all lowercase' do 98 | let(:string) { 'server_security_misconfiguration' } 99 | 100 | it { is_expected.to be_truthy } 101 | end 102 | 103 | context 'with numeric in front' do 104 | let(:string) { '2fa_example' } 105 | 106 | it { is_expected.to be_falsey } 107 | end 108 | 109 | context 'super short' do 110 | let(:string) { 'f' } 111 | 112 | it { is_expected.to be_truthy } 113 | end 114 | 115 | context 'with invalid characters' do 116 | let(:string) { 'a12112asdas2312dcdwsdf-^?' } 117 | 118 | it { is_expected.to be_falsey } 119 | end 120 | 121 | context 'when ending in a .' do 122 | let(:string) { 'server_security_misconfiguration.' } 123 | 124 | it { is_expected.to be_falsey } 125 | end 126 | 127 | context 'when a nested node' do 128 | let(:string) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } 129 | it { is_expected.to be_truthy } 130 | end 131 | 132 | context 'when an invalid nested node' do 133 | let(:string) { 'server_security_misconfiguration.meep_meep_meep' } 134 | it { is_expected.to be_falsey } 135 | end 136 | 137 | context 'when contains numbers' do 138 | let(:string) { 'sensitive_data_exposure.token_leakage_via_referer.untrusted_3rd_party' } 139 | it { is_expected.to be_truthy } 140 | end 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /spec/vrt/mappings_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VRT::Mapping do 4 | let(:vrt) { VRT } 5 | 6 | describe 'mapping files' do 7 | subject(:get_mapping) { described_class.new(scheme).get(id_list, version) } 8 | 9 | let(:id_list) { %i[server_security_misconfiguration] } 10 | let(:version) { '999.999' } 11 | 12 | context 'when mapping is nested under a subdirectory' do 13 | let(:scheme) { :cvss_v3 } 14 | 15 | it { is_expected.not_to be nil } 16 | end 17 | 18 | context 'when mapping is in flat directory' do 19 | let(:scheme) { :cwe } 20 | 21 | it { is_expected.not_to be nil } 22 | end 23 | 24 | context 'when mapping does not exist' do 25 | let(:scheme) { :not_a_valid_mapping } 26 | 27 | it 'fails to find the mapping' do 28 | expect { get_mapping }.to raise_error VRT::Errors::MappingNotFound 29 | end 30 | end 31 | end 32 | 33 | describe 'cvss_mapping' do 34 | let(:cvss) { described_class.new(:cvss_v3) } 35 | 36 | describe '#get' do 37 | subject { cvss.get(id_list, version) } 38 | 39 | context 'when cvss_v3 on leaf node' do 40 | let(:id_list) { %i[unvalidated_redirects_and_forwards open_redirect get_based] } 41 | let(:version) { '2.0' } 42 | 43 | it { is_expected.to eq('j') } 44 | end 45 | 46 | context 'when cvss_v3 on internal node' do 47 | let(:id_list) { %i[server_security_misconfiguration unsafe_cross_origin_resource_sharing critical_impact] } 48 | let(:version) { '2.0' } 49 | 50 | it { is_expected.to eq('b') } 51 | end 52 | 53 | context 'when cvss_v3 on root node with children' do 54 | let(:id_list) { %i[server_security_misconfiguration unsafe_file_upload no_antivirus] } 55 | let(:version) { '2.0' } 56 | 57 | it { is_expected.to eq('a') } 58 | end 59 | 60 | context 'when cvss_v3 on root node without children' do 61 | let(:id_list) { %i[insecure_data_storage] } 62 | let(:version) { '2.0' } 63 | 64 | it { is_expected.to eq('l') } 65 | end 66 | 67 | context 'when other' do 68 | let(:id_list) { %i[other] } 69 | let(:version) { '2.0' } 70 | 71 | it { is_expected.to eq('default') } 72 | end 73 | 74 | context 'when newer version' do 75 | let(:id_list) { %i[unvalidated_redirects_and_forwards open_redirect get_based] } 76 | let(:version) { '999.999' } 77 | 78 | it { is_expected.to eq('AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N') } 79 | end 80 | 81 | context 'when version predates the mapping' do 82 | context 'but id still exists' do 83 | let(:id_list) { %i[server_security_misconfiguration misconfigured_dns] } 84 | let(:version) { '1.0' } 85 | 86 | it 'gets the mapping from earliest version with mapping' do 87 | is_expected.to eq('e') 88 | end 89 | end 90 | 91 | context 'and id is deprecated' do 92 | let(:id_list) { %i[unvalidated_redirects_and_forwards open_redirect get_based_authenticated] } 93 | let(:version) { '1.0' } 94 | 95 | it 'follows deprecated_node_mapping' do 96 | is_expected.to eq('j') 97 | end 98 | end 99 | 100 | context 'and id is deprecated with valid parent' do 101 | let(:id_list) { %i[broken_authentication_and_session_management authentication_bypass horizontal] } 102 | let(:version) { '1.0' } 103 | 104 | it 'finds valid parent' do 105 | is_expected.to eq('m') 106 | end 107 | end 108 | 109 | context 'mapping with two keys' do 110 | let(:advice) { described_class.new(:remediation_advice) } 111 | let(:version) { '999.999' } 112 | 113 | subject { advice.get(id_list, version) } 114 | 115 | context 'when only one of the keys has values' do 116 | let(:id_list) { %i[server_security_misconfiguration] } 117 | 118 | it 'returns a hash with both mapping keys present' do 119 | is_expected.to match a_hash_including( 120 | remediation_advice: nil, 121 | references: [ 122 | 'https://www.owasp.org/index.php/Top_10_2013-A5-Security_Misconfiguration', 123 | 'http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration' 124 | ] 125 | ) 126 | end 127 | end 128 | 129 | context 'when both of the keys have values' do 130 | let(:id_list) { %i[server_security_misconfiguration unsafe_cross_origin_resource_sharing] } 131 | 132 | it 'returns a hash with both mapping keys and values present' do 133 | is_expected.to match a_hash_including( 134 | remediation_advice: 'This is advice', 135 | references: [ 136 | 'https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Cross_Origin_Resource_Sharing', 137 | 'https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS', 138 | 'https://www.owasp.org/index.php/Top_10_2013-A5-Security_Misconfiguration', 139 | 'http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration' 140 | ] 141 | ) 142 | end 143 | end 144 | 145 | context 'with arrays as the mapping values' do 146 | let(:id_list) { %i[server_security_misconfiguration misconfigured_dns subdomain_takeover] } 147 | 148 | it 'merges the arrays in order of variant -> subcategory -> category' do 149 | is_expected.to match a_hash_including( 150 | references: [ 151 | 'https://labs.detectify.com/2014/10/21/hostile-subdomain-takeover-using-herokugithubdesk-more/', 152 | 'https://www.owasp.org/index.php/Top_10_2013-A5-Security_Misconfiguration', 153 | 'http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration' 154 | ] 155 | ) 156 | end 157 | end 158 | 159 | context 'with a default' do 160 | subject { mapping.get(id_list, version) } 161 | 162 | let(:mapping) { described_class.new(:test_mapping) } 163 | let(:id_list) { %i[server_security_misconfiguration] } 164 | let(:version) { '999.999' } 165 | 166 | it { is_expected.to include(:one_thing, :another_thing) } 167 | 168 | context 'when falling back to default' do 169 | let(:id_list) { %i[broken_authentication_and_session_management] } 170 | 171 | it { is_expected.to include(:one_thing) } 172 | end 173 | end 174 | end 175 | 176 | context 'with arrays as the mapping values' do 177 | subject { described_class.new(:cwe).get(id_list, version) } 178 | let(:version) { '999.999' } 179 | 180 | context 'when mapping has a default' do 181 | let(:id_list) { %i[server_security_misconfiguration] } 182 | 183 | it 'does NOT include the default mapping value' do 184 | is_expected.to contain_exactly('CWE-933') 185 | end 186 | 187 | context 'no mapping exists' do 188 | let(:id_list) { %i[other] } 189 | 190 | it 'only includes the default mapping value' do 191 | is_expected.to contain_exactly('CWE-2000') 192 | end 193 | end 194 | end 195 | end 196 | end 197 | end 198 | end 199 | end 200 | -------------------------------------------------------------------------------- /spec/vrt/node_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VRT::Node do 4 | describe '#children?' do 5 | subject(:has_children) { VRT::Map.new(version).find_node(id).children? } 6 | 7 | let(:version) { '2.0' } 8 | 9 | context 'when node has children' do 10 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } 11 | 12 | it { is_expected.to be true } 13 | end 14 | 15 | context 'when node does not have children' do 16 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing.high_impact' } 17 | 18 | it { is_expected.to be false } 19 | end 20 | end 21 | 22 | describe 'children' do 23 | subject(:children) { VRT::Map.new(version).find_node(id).children } 24 | 25 | let(:version) { '2.0' } 26 | 27 | context 'when node has children' do 28 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } 29 | 30 | it 'should contain a list of child nodes' do 31 | expect(children.length).to eq 2 32 | end 33 | end 34 | 35 | context 'when node does not have children' do 36 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing.high_impact' } 37 | 38 | it { is_expected.to be_empty } 39 | end 40 | end 41 | 42 | describe '#mappings' do 43 | subject(:mappings) { VRT::Map.new(version).find_node(id).mappings } 44 | 45 | let(:version) { '2.0' } 46 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing.high_impact' } 47 | 48 | it 'returns a hash with the correct keys' do 49 | expect(mappings.keys).to eq(VRT::MAPPINGS) 50 | end 51 | 52 | context 'cvss_v3' do 53 | it 'has the right values' do 54 | expect(mappings).to include(cvss_v3: 'b') 55 | end 56 | end 57 | 58 | context 'remediation_advice' do 59 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } 60 | 61 | it 'has the expected remediation advice' do 62 | expect(mappings[:remediation_advice]).to match hash_including( 63 | remediation_advice: 'This is advice' 64 | ) 65 | end 66 | 67 | it 'has the expected (concatenated) references' do 68 | expect(mappings[:remediation_advice]).to match hash_including( 69 | references: [ 70 | 'https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Cross_Origin_Resource_Sharing', 71 | 'https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS', 72 | 'https://www.owasp.org/index.php/Top_10_2013-A5-Security_Misconfiguration', 73 | 'http://projects.webappsec.org/w/page/13246959/Server%20Misconfiguration' 74 | ] 75 | ) 76 | end 77 | end 78 | 79 | context 'cwe' do 80 | it 'has the exepected (concatenated) CWE IDs' do 81 | expect(mappings[:cwe]).to eq %w[ 82 | CWE-942 83 | CWE-933 84 | ] 85 | end 86 | end 87 | end 88 | 89 | describe '#third_party_links' do 90 | subject(:third_party_links) { VRT::Map.new(version).find_node(id).third_party_links } 91 | 92 | let(:version) { '2.0' } 93 | let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } 94 | 95 | it { is_expected.to include(:scw) } 96 | 97 | it 'loads correct mapping' do 98 | expect(third_party_links[:scw]).to eq 'https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:unsafe_cross_origin_resource_sharing&redirect=true' 99 | end 100 | end 101 | 102 | describe '#as_json' do 103 | subject(:node_hash) { VRT::Map.new(version).find_node(id).as_json } 104 | 105 | let(:version) { '2.0' } 106 | 107 | context 'when node is a parent' do 108 | let(:id) { 'server_security_misconfiguration' } 109 | 110 | it 'should return nil parent id' do 111 | expect(node_hash['parent']).to be_nil 112 | expect(node_hash['qualified_vrt_id']).to eq id 113 | expect(node_hash['children'].length).to eq 9 114 | end 115 | end 116 | 117 | context 'when node is a leaf' do 118 | let(:id) { 'unvalidated_redirects_and_forwards.open_redirect.get_based' } 119 | 120 | it 'should return the full parent id and no children' do 121 | expect(node_hash['parent']).to eq 'unvalidated_redirects_and_forwards.open_redirect' 122 | expect(node_hash['children']).to be_empty 123 | expect(node_hash['has_children']).to be_falsey 124 | expect(node_hash['qualified_vrt_id']).to eq id 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /spec/vrt/third_party_links_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VRT::ThirdPartyLinks do 4 | let(:vrt) { VRT } 5 | 6 | describe '#get' do 7 | subject(:get_mapping) { described_class.new(scheme, directory).get(id_list, version) } 8 | 9 | let(:id_list) { %i[server_security_misconfiguration directory_listing_enabled sensitive_data_exposure] } 10 | let(:directory) { nil } 11 | let(:scheme) { 'secure-code-warrior-links' } 12 | let(:version) { '999.999' } 13 | 14 | context 'when mapping is nested under a subdirectory' do 15 | let(:directory) { 'remediation_training' } 16 | let(:version) { '2.0' } 17 | 18 | it 'gives the third party link for the id_list' do 19 | expect(subject).to eq('https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:directory_listing_enabled:sensitive_data_exposure&redirect=true') 20 | end 21 | end 22 | 23 | context 'when mapping is under a flat directory' do 24 | it 'gives the third party link for the id_list' do 25 | expect(subject).to eq('https://integration-api.securecodewarrior.com/api/v1/trial?id=bugcrowd&mappingList=vrt&mappingKey=server_security_misconfiguration:directory_listing_enabled:sensitive_data_exposure&redirect=true') 26 | end 27 | end 28 | 29 | context 'for invalid scheme' do 30 | let(:scheme) { :not_a_valid_mapping } 31 | 32 | it 'fails to find the mapping' do 33 | expect { get_mapping }.to raise_error VRT::Errors::MappingNotFound 34 | end 35 | end 36 | 37 | context 'if the id_list is invalid' do 38 | let(:id_list) { %i[server_security_misconfiguration complete_gibbrish] } 39 | 40 | it 'returns nil' do 41 | expect(get_mapping).to be_nil 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/vrt_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VRT do 4 | let(:dir) { described_class::DIR } 5 | 6 | # Make sure we're not caching values in memory from previous specs. ;) 7 | before { described_class.reload! } 8 | 9 | # Make sure specs that run after us don't inherit our stubbed-out, cached 10 | # values 11 | after { described_class.unload! } 12 | 13 | describe '#versions' do 14 | it 'return all of the versions in the filesystem in reverse semver order' do 15 | expect(described_class.versions).to eq(['999.999', '2.0', '1.0']) 16 | end 17 | end 18 | 19 | describe '#current_version' do 20 | it 'return the most recent version number for the vrt' do 21 | expect(described_class.current_version).to eq('999.999') 22 | end 23 | end 24 | 25 | describe '#get_json' do 26 | it 'takes a vrt version number and returns parsed json data from the appropriate file' do 27 | expect(described_class.get_json(version: '1.0')).to be_a(Array) 28 | end 29 | 30 | it 'adds an Other category to the VRT if other is true' do 31 | expect(described_class.get_json(version: '1.0', other: true)).to include(described_class::OTHER_OPTION) 32 | end 33 | end 34 | 35 | describe '#get_map' do 36 | it 'takes a vrt version number and returns a VRT::Map' do 37 | expect(described_class.get_map(version: '1.0')).to be_a(VRT::Map) 38 | end 39 | 40 | it 'only creates the map once' do 41 | expect(VRT::Map).to receive(:new).once.and_return('dummy map') 42 | expect(described_class.get_map(version: '1.0')).to eq('dummy map') 43 | expect(described_class.get_map(version: '1.0')).to eq('dummy map') 44 | end 45 | end 46 | 47 | describe '#last_update' do 48 | it 'shows the last updated time from version metadata' do 49 | expect(described_class.last_updated).to eq Date.parse('Tue, 17 Feb 3001') 50 | end 51 | end 52 | 53 | describe '#current_categories' do 54 | subject { described_class.current_categories } 55 | 56 | let!(:category) do 57 | subject.select { |cat| cat[:value] == :server_security_misconfiguration }.first 58 | end 59 | 60 | it 'uses the newest names for the same key' do 61 | expect(category[:label]).to eq 'Server Security Misconfiguration (1.1)' 62 | end 63 | 64 | it 'does not contain duplicate keys' do 65 | expect(subject.uniq { |cat| cat[:value] }).to eq subject 66 | end 67 | end 68 | 69 | describe '#find_node' do 70 | subject(:found_node) do 71 | described_class.find_node( 72 | vrt_id:, 73 | preferred_version: new_version, 74 | max_depth: 75 | ) 76 | end 77 | 78 | let(:new_version) { nil } 79 | let(:max_depth) { 'variant' } 80 | 81 | context 'when vrt_id exists' do 82 | let(:vrt_id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } 83 | 84 | context 'and no preferred version given' do 85 | it 'should return the node with the same id in the latest version' do 86 | expect(found_node.qualified_vrt_id).to eq vrt_id 87 | expect(found_node.version).to eq described_class.current_version 88 | end 89 | end 90 | 91 | context 'and preferred version given' do 92 | let(:new_version) { '2.0' } 93 | 94 | it 'should return the node with the same id in the preferred version' do 95 | expect(found_node.qualified_vrt_id).to eq vrt_id 96 | expect(found_node.version).to eq new_version 97 | end 98 | end 99 | 100 | context 'and max_depth given' do 101 | let(:max_depth) { 'category' } 102 | 103 | it 'should return the node in the latest version at given max_depth' do 104 | expect(found_node.qualified_vrt_id).to eq vrt_id.split('.')[0] 105 | expect(found_node.version).to eq described_class.current_version 106 | end 107 | end 108 | end 109 | 110 | context 'when vrt_id is deprecated' do 111 | let(:vrt_id) { 'unvalidated_redirects_and_forwards.open_redirect.get_based_authenticated' } 112 | let(:expected_found_id) { 'unvalidated_redirects_and_forwards.open_redirect.get_based' } 113 | 114 | context 'and has mapping in preferred version' do 115 | let(:new_version) { '2.0' } 116 | 117 | it 'should return the node mapped from the deprecated node in the preferred version' do 118 | expect(found_node.qualified_vrt_id).to eq expected_found_id 119 | expect(found_node.version).to eq new_version 120 | end 121 | end 122 | 123 | context 'but given preferred is newer than deprecated entry' do 124 | let(:new_version) { '999.999' } 125 | 126 | it 'should return the node mapped from the deprecated node in the preferred version' do 127 | expect(found_node.qualified_vrt_id).to eq expected_found_id 128 | expect(found_node.version).to eq new_version 129 | end 130 | end 131 | 132 | context 'due to category change and max_depth is supplied' do 133 | let(:vrt_id) { 'unvalidated_redirects_and_forwards.lack_of_security_speed_bump_page' } 134 | let(:max_depth) { 'category' } 135 | 136 | it 'should return the node mapped from the deprecated node in the preferred version' do 137 | expect(found_node.qualified_vrt_id).to eq 'external_behavior' 138 | expect(found_node.version).to eq described_class.current_version 139 | end 140 | end 141 | end 142 | 143 | context 'when vrt_id is not found in deprecated node mapping' do 144 | context 'and has a valid parent node' do 145 | let(:vrt_id) { 'sensitive_data_exposure.token_leakage_via_referer.over_https' } 146 | let(:new_version) { '999.999' } 147 | 148 | it 'should return the most relevant parent node' do 149 | expect(found_node.qualified_vrt_id).to eq 'sensitive_data_exposure.token_leakage_via_referer' 150 | end 151 | 152 | context 'with max_depth given' do 153 | let(:max_depth) { 'category' } 154 | 155 | it 'should return the parent node at the specified depth' do 156 | expect(found_node.qualified_vrt_id).to eq 'sensitive_data_exposure' 157 | end 158 | end 159 | end 160 | 161 | context 'has valid parent two levels up' do 162 | let(:vrt_id) { 'insecure_data_storage.insecure_data_storage.password' } 163 | let(:new_version) { '999.999' } 164 | 165 | it 'should return top level category' do 166 | expect(found_node.qualified_vrt_id).to eq 'insecure_data_storage' 167 | end 168 | end 169 | 170 | context 'and has no valid parent node' do 171 | let(:vrt_id) { 'sensitive_data_exposure.token_leakage_via_referer.over_https' } 172 | let(:new_version) { '2.0' } 173 | 174 | it 'should return nil' do 175 | expect(found_node).to be_nil 176 | end 177 | end 178 | 179 | context 'when found node is also deprecated' do 180 | let(:vrt_id) { 'unvalidated_redirects_and_forwards.open_redirect.get_based_all_users' } 181 | let(:new_version) { '999.999' } 182 | 183 | it 'should retrieve the deeply nested deprecated mapping' do 184 | expect(found_node.qualified_vrt_id).to eq 'unvalidated_redirects_and_forwards.open_redirect.get_based' 185 | end 186 | end 187 | end 188 | end 189 | 190 | describe '#all_matching_categories' do 191 | subject(:full_search_list) { described_class.all_matching_categories(categories) } 192 | 193 | context 'with deprecated other category' do 194 | let(:categories) { %w[external_behavior other] } 195 | let(:deprecated) do 196 | %w[ 197 | poor_physical_security 198 | social_engineering 199 | unvalidated_redirects_and_forwards.lack_of_security_speed_bump_page 200 | ] 201 | end 202 | 203 | it 'should return a list containing the deprecated categories' do 204 | expect(full_search_list).to contain_exactly(*deprecated) 205 | end 206 | end 207 | 208 | context 'with subcategories included' do 209 | let(:categories) do 210 | %w[ 211 | external_behavior 212 | other 213 | unvalidated_redirects_and_forwards.open_redirect 214 | ] 215 | end 216 | let(:deprecated) do 217 | %w[ 218 | poor_physical_security 219 | social_engineering 220 | unvalidated_redirects_and_forwards.lack_of_security_speed_bump_page 221 | unvalidated_redirects_and_forwards.open_redirect.get_based_all_users 222 | unvalidated_redirects_and_forwards.open_redirect.get_based_unauthenticated 223 | unvalidated_redirects_and_forwards.open_redirect.get_based_authenticated 224 | unvalidated_redirects_and_forwards.open_redirect.get_based_thingo 225 | ] 226 | end 227 | 228 | it 'should return a list containing the deprecated categories' do 229 | expect(full_search_list).to contain_exactly(*deprecated) 230 | end 231 | end 232 | end 233 | 234 | describe '#current_version?' do 235 | subject(:current_version) { described_class.current_version?(version) } 236 | 237 | context 'when version is current' do 238 | let(:version) { '999.999' } 239 | 240 | it { is_expected.to be } 241 | end 242 | 243 | context 'when version is not current' do 244 | let(:version) { '2.0' } 245 | 246 | it { is_expected.to be false } 247 | end 248 | end 249 | 250 | describe '#mappings' do 251 | subject(:mappings) { described_class.mappings } 252 | 253 | it { is_expected.to include(*VRT::MAPPINGS) } 254 | end 255 | 256 | describe '#third_party_links' do 257 | subject(:third_party_links) { described_class.third_party_links } 258 | 259 | it { is_expected.to include(:scw) } 260 | end 261 | end 262 | -------------------------------------------------------------------------------- /vrt.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('lib', __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'date' 4 | require 'vrt/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'vrt' 8 | spec.version = Vrt::VERSION 9 | spec.platform = Gem::Platform::RUBY 10 | spec.authors = ['Barnett Klane', 'Max Schwenk', 'Adam David', 11 | 'Abhinav Nain'] 12 | spec.email = ['barnett@bugcrowd.com', 'max.schwenk@bugcrowd.com', 13 | 'adam.david@bugcrowd.com', 14 | 'abhinav.nain@bugcrowd.com'] 15 | spec.date = Date.today.to_s 16 | spec.summary = "Ruby wrapper for Bugcrowd\'s Vulnerability Rating Taxonomy" 17 | spec.homepage = 'https://github.com/bugcrowd/vrt-ruby' 18 | spec.license = 'MIT' 19 | spec.files = Dir['lib/**/*.{rb,json}'] 20 | spec.require_paths = ['lib'] 21 | spec.required_ruby_version = '>= 3.1' 22 | 23 | spec.add_development_dependency 'bundler', '~> 2.5.14' 24 | spec.add_development_dependency 'pry', '~> 0.14.2' 25 | spec.add_development_dependency 'rake', '~> 13.2.1' 26 | spec.add_development_dependency 'rspec', '~> 3.13' 27 | # TODO: investigate why rubocop's jaro-winkler dependency fails to install in our alpine linux image 28 | spec.add_development_dependency 'rubocop', '1.52.1' 29 | spec.metadata = { 30 | 'homepage_uri' => 'https://github.com/bugcrowd/vrt-ruby', 31 | 'changelog_uri' => 'https://github.com/bugcrowd/vrt-ruby/blob/master/CHANGELOG.md', 32 | 'source_code_uri' => 'https://github.com/bugcrowd/vrt-ruby', 33 | 'bug_tracker_uri' => 'https://github.com/bugcrowd/vrt-ruby/issues' 34 | } 35 | end 36 | --------------------------------------------------------------------------------