├── api ├── conftest.py ├── tp_api │ ├── __init__.py │ ├── swagger │ │ ├── get-project.yml │ │ ├── change-password.yml │ │ ├── get-feature.yml │ │ ├── get-abuse.yml │ │ ├── get-tests.yml │ │ ├── get-scenario.yml │ │ ├── scan-create.yml │ │ ├── feature-create.yml │ │ ├── abuse-create.yml │ │ ├── target-create-update.yml │ │ ├── scenario-repo.yml │ │ ├── test-case-create.yml │ │ ├── scenario-inline.yml │ │ └── vulnerability-create.yml │ ├── repo │ │ ├── weak-default-password.yaml │ │ ├── password-bruteforce.yaml │ │ ├── aws_s3.yaml │ │ ├── ssrf.yaml │ │ ├── cert_validation.yaml │ │ ├── plaintext-transmission.yaml │ │ ├── xxe.yaml │ │ ├── idor_pk.yaml │ │ ├── insecure_deserialization.yaml │ │ ├── sql_injection.yaml │ │ ├── code_injection.yaml │ │ └── xss.yaml │ ├── wait-for │ ├── utils.py │ └── models.py ├── .vscode │ └── settings.json ├── Dockerfile └── requirements.txt ├── documentation ├── static │ ├── .nojekyll │ └── img │ │ ├── login.png │ │ ├── logo.png │ │ ├── favicon.ico │ │ ├── dashboard.png │ │ ├── logo.svg │ │ └── undraw_docusaurus_tree.svg ├── babel.config.js ├── .gitignore ├── sidebars.js ├── docs │ ├── client-ui.md │ ├── tutorial-client-ui.md │ ├── overview.md │ ├── quick-installation.md │ ├── configure-cli.md │ ├── install-client.md │ ├── create-project.md │ ├── tutorial.md │ └── story-driven.md ├── src │ ├── pages │ │ ├── styles.module.css │ │ └── index.js │ └── css │ │ └── custom.css ├── package.json ├── README.md └── docusaurus.config.js ├── .dockerignore ├── img ├── tp-logo.png └── we45logo.jpg ├── frontend └── Playbook-Frontend │ ├── store │ ├── index.js │ ├── abuserStory.js │ ├── threatmap.js │ ├── projects.js │ ├── home.js │ ├── login.js │ ├── scan.js │ ├── userStory.js │ ├── threatScenario.js │ └── vulnerability.js │ ├── static │ ├── tp-logo.png │ └── threat_logo.png │ ├── assets │ ├── threat_logo.png │ └── variables.scss │ ├── Dockerfile │ ├── plugins │ ├── vue-apexchart.js │ ├── vue-organization-chart.js │ └── README.md │ ├── components │ ├── README.md │ ├── VuetifyLogo.vue │ ├── Logo.vue │ ├── project │ │ ├── Content.vue │ │ └── Header.vue │ └── projects │ │ └── ProjectsPage.vue │ ├── .editorconfig │ ├── jsconfig.json │ ├── test │ └── Logo.spec.js │ ├── .babelrc │ ├── middleware │ └── README.md │ ├── layouts │ ├── default.vue │ ├── error.vue │ └── main.vue │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── pages │ ├── projects │ │ ├── index.vue │ │ └── _name │ │ │ ├── vulnerabilities.vue │ │ │ ├── project.vue │ │ │ ├── threat-map.vue │ │ │ └── user-story.vue │ ├── scan │ │ └── _name.vue │ ├── index.vue │ └── home │ │ └── index.vue │ ├── .gitignore │ └── nuxt.config.js ├── nginx ├── alpine.df ├── Dockerfile └── sites-enabled │ └── tp_nginx ├── .github └── workflows │ └── main.yml ├── README.md ├── docker-compose.yml └── .gitignore /api/conftest.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/tp_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /documentation/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /img/tp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/img/tp-logo.png -------------------------------------------------------------------------------- /img/we45logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/img/we45logo.jpg -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/index.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({}) 2 | export const getters = {} 3 | -------------------------------------------------------------------------------- /documentation/static/img/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/documentation/static/img/login.png -------------------------------------------------------------------------------- /documentation/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/documentation/static/img/logo.png -------------------------------------------------------------------------------- /api/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.formatting.provider": "black", 3 | "python.pythonPath": "venv/bin/python" 4 | } -------------------------------------------------------------------------------- /documentation/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/documentation/static/img/favicon.ico -------------------------------------------------------------------------------- /documentation/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /documentation/static/img/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/documentation/static/img/dashboard.png -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/static/tp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/frontend/Playbook-Frontend/static/tp-logo.png -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/assets/threat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/frontend/Playbook-Frontend/assets/threat_logo.png -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/static/threat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we45/ThreatPlaybook/HEAD/frontend/Playbook-Frontend/static/threat_logo.png -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | COPY dist ./dist 4 | 5 | RUN npm install -g serve 6 | ENV PORT=80 7 | CMD serve -s dist -p 80 8 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/plugins/vue-apexchart.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueApexCharts from "vue-apexcharts"; 3 | 4 | Vue.use({ 5 | install(Vue, options) { 6 | Vue.component("apexchart", VueApexCharts); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /nginx/alpine.df: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | 3 | RUN echo "daemon off;" >> /etc/nginx/nginx.conf 4 | RUN rm /etc/nginx/sites-enabled/* 5 | 6 | ADD sites-enabled/ /etc/nginx/sites-enabled/ 7 | 8 | EXPOSE 5000 9 | EXPOSE 80 10 | 11 | CMD ["/usr/sbin/nginx"] 12 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "~/*": ["./*"], 6 | "@/*": ["./*"], 7 | "~~/*": ["./*"], 8 | "@@/*": ["./*"] 9 | } 10 | }, 11 | "exclude": ["node_modules", ".nuxt", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/test/Logo.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import Logo from '@/components/Logo.vue' 3 | 4 | describe('Logo', () => { 5 | test('is a Vue instance', () => { 6 | const wrapper = mount(Logo) 7 | expect(wrapper.isVueInstance()).toBeTruthy() 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": [ 5 | [ 6 | "@babel/preset-env", 7 | { 8 | "targets": { 9 | "node": "current" 10 | } 11 | } 12 | ] 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/plugins/vue-organization-chart.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import OrganizationChart from "vue-organization-chart"; 3 | import "vue-organization-chart/dist/orgchart.css"; 4 | 5 | Vue.use({ 6 | install(Vue, options) { 7 | Vue.component("OrganizationChart", OrganizationChart); 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /documentation/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /documentation/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | someSidebar: { 3 | QuickStart: [ 4 | "overview", 5 | "installation", 6 | "client-install", 7 | "configure-cli", 8 | "create-project", 9 | "client-ui", 10 | ], 11 | Tutorial: ["tutorial", "story-driven", "tutorial-client-ui"], 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN apt-get update && apt-get install -y nginx && apt-get clean && rm -rf /var/lib/apt/lists/* 4 | 5 | RUN echo "daemon off;" >> /etc/nginx/nginx.conf 6 | RUN rm /etc/nginx/sites-enabled/default 7 | 8 | ADD sites-enabled/ /etc/nginx/sites-enabled/ 9 | 10 | EXPOSE 5000 11 | EXPOSE 80 12 | 13 | CMD ["/usr/sbin/nginx"] 14 | -------------------------------------------------------------------------------- /documentation/docs/client-ui.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: client-ui 3 | title: Web UI 4 | sidebar_label: Web UI 5 | --- 6 | 7 | #### Open Addressable IP Address on the browser 8 | 9 | ![Image](/img/login.png) 10 | 11 | 12 | >**Note:** Login using credentials 13 | 14 | 15 | ##### Once you logged in you should see a dashboard of Threat Playbook 16 | 17 | ![Image](/img/dashboard.png) -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /documentation/docs/tutorial-client-ui.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: tutorial-client-ui 3 | title: Web UI 4 | sidebar_label: Web UI 5 | --- 6 | 7 | #### Open Addressable IP Address on the browser 8 | 9 | ![Image](/img/login.png) 10 | 11 | 12 | >**Note:** Login using credentials 13 | 14 | 15 | ##### Once you logged in you should see a dashboard of Threat Playbook 16 | 17 | ![Image](/img/dashboard.png) -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/components/VuetifyLogo.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/assets/variables.scss: -------------------------------------------------------------------------------- 1 | // Ref: https://github.com/nuxt-community/vuetify-module#customvariables 2 | // 3 | // The variables you want to modify 4 | // $font-size-root: 20px; 5 | // assets/variables.scss 6 | 7 | // Variables you want to modify 8 | $btn-border-radius: 0px; 9 | 10 | // If you need to extend Vuetify SASS lists 11 | // $material-light: ( cards: blue ); 12 | @import '~vuetify/src/styles/styles.sass'; -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /api/tp_api/swagger/get-project.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used retrieve a single Project or multiple Projects 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | schema: 7 | id: Project 8 | type: object 9 | properties: 10 | name: 11 | type: string 12 | 13 | responses: 14 | 200: 15 | description: Project or Projects successfully retrieved 16 | 404: 17 | description: Unable to find Scan or Target 18 | 19 | -------------------------------------------------------------------------------- /documentation/docs/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: overview 3 | title: Overview 4 | sidebar_label: Overview 5 | --- 6 | 7 | #### Threat Playbook has 3 components 8 | 1. Threat Playbook Server 9 | 2. Threat Playbook Client 10 | 3. Threat Playbook WebUI 11 | 12 | 13 | 14 | #### Prerequisite 15 | * Docker and Docker-Compose 16 | 17 | #### Container Images 18 | 19 | * API Container Image - Python Flask 20 | * MongoDB Container Image 21 | * VueJS Front-end Container Image 22 | * Nginx Reverse Proxy 23 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/README.md: -------------------------------------------------------------------------------- 1 | # Playbook-Frontend 2 | 3 | > My swell Nuxt.js project 4 | 5 | ## Build Setup 6 | 7 | ```bash 8 | # install dependencies 9 | $ yarn install 10 | 11 | # serve with hot reload at localhost:3000 12 | $ yarn dev 13 | 14 | # build for production and launch server 15 | $ yarn build 16 | $ yarn start 17 | 18 | # generate static project 19 | $ yarn generate 20 | ``` 21 | 22 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 23 | -------------------------------------------------------------------------------- /api/tp_api/swagger/change-password.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to change an existing user's password in ThreatPlaybook 2 | --- 3 | requestBody: 4 | required: true 5 | content: 6 | application/json: 7 | schema: 8 | type: object 9 | properties: 10 | email: 11 | type: string 12 | old_password: 13 | type: string 14 | new_password: 15 | type: string 16 | verify_password: 17 | type: string 18 | 19 | 20 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:alpine3.7 2 | COPY tp_api/ /app 3 | COPY requirements.txt /app 4 | ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait ./wait 5 | RUN chmod +x ./wait 6 | WORKDIR /app 7 | RUN chmod +x wait-for 8 | RUN apk add --no-cache --virtual .build-deps g++ python3-dev libffi-dev libressl-dev && \ 9 | apk add --no-cache --update python3 && \ 10 | pip3 install --upgrade pip setuptools 11 | RUN pip install -r requirements.txt 12 | EXPOSE 5000 13 | CMD ["/usr/local/bin/python", "/app/app.py"] -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleNameMapper: { 3 | '^@/(.*)$': '/$1', 4 | '^~/(.*)$': '/$1', 5 | '^vue$': 'vue/dist/vue.common.js' 6 | }, 7 | moduleFileExtensions: [ 8 | 'js', 9 | 'vue', 10 | 'json' 11 | ], 12 | transform: { 13 | '^.+\\.js$': 'babel-jest', 14 | '.*\\.(vue)$': 'vue-jest' 15 | }, 16 | collectCoverage: true, 17 | collectCoverageFrom: [ 18 | '/components/**/*.vue', 19 | '/pages/**/*.vue' 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /api/tp_api/swagger/get-feature.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used retrieve a single Feature/User Story or multiple Features/User Stories 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | schema: 7 | id: Feature 8 | type: object 9 | properties: 10 | name: 11 | type: string 12 | page: 13 | type: integer 14 | project: 15 | type: string 16 | 17 | responses: 18 | 200: 19 | description: Project or Projects successfully retrieved 20 | 404: 21 | description: Unable to find Scan or Target 22 | 23 | -------------------------------------------------------------------------------- /documentation/docs/quick-installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Install Server 4 | sidebar_label: Install Server 5 | --- 6 | 7 | ##### Download docker-compose file 8 | ``` 9 | git clone https://github.com/we45/ThreatPlaybook.git 10 | ``` 11 | 12 | ##### `cd` into the downloaded directory 13 | ``` 14 | cd ThreatPlaybook 15 | ``` 16 | 17 | > For Linux users, you might have to add another set of permissions for the MongoDB to work correctly. 18 | > 19 | > `sudo chown -R 1001 $PWD/playbook` 20 | 21 | ##### Run the Playbook server 22 | ``` 23 | docker-compose up -d 24 | ``` 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /api/tp_api/swagger/get-abuse.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used retrieve a single Abuser Story or multiple Abuser Stories 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Abuser Story 9 | type: object 10 | required: 11 | - feature 12 | properties: 13 | name: 14 | type: string 15 | page: 16 | type: integer 17 | feature: 18 | type: string 19 | 20 | responses: 21 | 200: 22 | description: Project or Projects successfully retrieved 23 | 404: 24 | description: Unable to find Scan or Target 25 | 26 | -------------------------------------------------------------------------------- /api/tp_api/swagger/get-tests.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used retrieve a single Abuser Story or multiple Abuser Stories 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Security Test Case 9 | type: object 10 | required: 11 | - scenario 12 | properties: 13 | name: 14 | type: string 15 | page: 16 | type: integer 17 | scenario: 18 | type: string 19 | 20 | responses: 21 | 200: 22 | description: Project or Projects successfully retrieved 23 | 404: 24 | description: Unable to find Threat Scenario or Test Cases 25 | 26 | -------------------------------------------------------------------------------- /api/tp_api/swagger/get-scenario.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used retrieve a single Abuser Story or multiple Abuser Stories 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Threat Scenario 9 | type: object 10 | required: 11 | - abuser_story 12 | properties: 13 | name: 14 | type: string 15 | page: 16 | type: integer 17 | abuser_story: 18 | type: string 19 | 20 | responses: 21 | 200: 22 | description: Threat Scenario or Scenarios successfully retrieved 23 | 404: 24 | description: Unable to find Threat Scenario or Abuser Story 25 | 26 | -------------------------------------------------------------------------------- /api/tp_api/swagger/scan-create.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to create a scan for storing Vulnerability Results in the Database 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Scan 9 | type: object 10 | required: 11 | - tool 12 | - target 13 | properties: 14 | tool: 15 | type: string 16 | target: 17 | type: string 18 | scan_type: 19 | type: string 20 | responses: 21 | 200: 22 | description: Scan successfully created 23 | 400: 24 | description: Input Validation Errors or other client Errors 25 | 404: 26 | description: Unable to find Target 27 | 28 | -------------------------------------------------------------------------------- /api/tp_api/repo/weak-default-password.yaml: -------------------------------------------------------------------------------- 1 | name: Weak/Default Passwords 2 | cwe: 521 3 | categories: [app_vulns,owasp] 4 | description: The application allows the users to set default or weak passwords 5 | mitigations: 6 | - description: Ensure that passwords set in the application are mandatorily strong. 7 | phase: Architecture and Design 8 | - description: Prevent Credential Stuffing by validating user passwords against Database of compromised passwords 9 | phase: Architecture and Design 10 | test-cases: 11 | - name: automated-vulnerability-scanning 12 | test: run automated vulnerability discovery tools and bruteforce against the application 13 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 14 | type: discovery -------------------------------------------------------------------------------- /documentation/docs/configure-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: configure-cli 3 | title: Configure CLI 4 | sidebar_label: Configure CLI 5 | --- 6 | 7 | 8 | ##### Create a Project Directory 9 | ``` 10 | mkdir -p /your/project/directory 11 | ``` 12 | 13 | ##### `cd` into your project directory 14 | ``` 15 | cd /your/project/directory 16 | ``` 17 | 18 | ##### Configure 19 | 20 | ``` 21 | playbook configure -e -u -p 22 | ``` 23 | 24 | > Please ensure that you substitute the following: 25 | > * `your-email` enter any email address, eample: admin@admin.com 26 | > * `host-info` for your addressable IP Address, example: 192.168.1.17 27 | > * `port` for the nginx port, example: 80 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /documentation/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | text-align: center; 11 | position: relative; 12 | overflow: hidden; 13 | } 14 | 15 | @media screen and (max-width: 966px) { 16 | .heroBanner { 17 | padding: 2rem; 18 | } 19 | } 20 | 21 | .buttons { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | .features { 28 | display: flex; 29 | align-items: center; 30 | padding: 2rem 0; 31 | width: 100%; 32 | } 33 | 34 | .featureImage { 35 | height: 200px; 36 | width: 200px; 37 | } 38 | -------------------------------------------------------------------------------- /api/tp_api/swagger/feature-create.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to create and update a User Story/Feature. 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Feature 9 | type: object 10 | required: 11 | - short_name 12 | - description 13 | - project 14 | properties: 15 | short_name: 16 | type: string 17 | description: 18 | type: string 19 | project: 20 | type: string 21 | responses: 22 | 200: 23 | description: Feature/User Story successfully created 24 | 400: 25 | description: Input Validation Errors or other client Errors 26 | 404: 27 | description: Unable to find Project 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Build API Docker Container 13 | run: cd $GITHUB_WORKSPACE/api/ && docker build . --file $GITHUB_WORKSPACE/api/Dockerfile --tag we45/threatplaybook-server 14 | - name: Test Secret 15 | run: echo $DOCKER_LOGIN_USER 16 | - name: Build and Push 17 | uses: zemuldo/docker-build-push@master 18 | env: 19 | DOCKER_USERNAME: "we45" 20 | DOCKER_NAMESPACE: "we45" 21 | DOCKER_PASSWORD: ${{ secrets.DOCKER_LOGIN_PASS }} 22 | REGISTRY_URL: "docker.io" 23 | with: 24 | image_name: "threatplaybook-server" 25 | image_tag: "latest" 26 | -------------------------------------------------------------------------------- /api/tp_api/swagger/abuse-create.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to create/update Abuser Stories linked to Feature/User Story 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Abuser Story 9 | type: object 10 | required: 11 | - short_name 12 | - description 13 | - feature 14 | properties: 15 | short_name: 16 | type: string 17 | description: 18 | type: string 19 | feature: 20 | type: string 21 | responses: 22 | 200: 23 | description: Abuser Story successfully created 24 | 400: 25 | description: Input Validation Errors or other client Errors 26 | 404: 27 | description: Unable to find Feature/User Story 28 | 29 | -------------------------------------------------------------------------------- /documentation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documentation", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy" 10 | }, 11 | "dependencies": { 12 | "@docusaurus/core": "^2.0.0-alpha.58", 13 | "@docusaurus/preset-classic": "^2.0.0-alpha.58", 14 | "clsx": "^1.1.1", 15 | "react": "^16.8.4", 16 | "react-dom": "^16.8.4" 17 | }, 18 | "browserslist": { 19 | "production": [ 20 | ">0.2%", 21 | "not dead", 22 | "not op_mini all" 23 | ], 24 | "development": [ 25 | "last 1 chrome version", 26 | "last 1 firefox version", 27 | "last 1 safari version" 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /documentation/docs/install-client.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: client-install 3 | title: Install Client 4 | sidebar_label: Install Client 5 | --- 6 | 7 | ##### Mac 8 | 9 | ``` 10 | wget -O playbook https://github.com/we45/ThreatPlaybook-ClientV3/raw/master/dist/playbook_darwin64 && chmod +x playbook && mv playbook /usr/local/bin 11 | ``` 12 | 13 | ##### Linux 14 | 15 | ``` 16 | wget -O https://github.com/we45/ThreatPlaybook-ClientV3/raw/master/dist/playbook_linux64 playbook && chmod +x playbook && mv playbook /usr/bin 17 | ``` 18 | 19 | ##### Windows 20 | 21 | ``` 22 | https://github.com/we45/ThreatPlaybook-ClientV3/raw/master/dist/playbook_windows64.exe 23 | ``` 24 | 25 | > Please note that the CLI hasn't been tested on Windows OS 26 | 27 | 28 | 29 | #### Verify the cli installation 30 | 31 | ``` 32 | playbook -h 33 | ``` 34 | -------------------------------------------------------------------------------- /api/tp_api/repo/password-bruteforce.yaml: -------------------------------------------------------------------------------- 1 | name: Bruteforce Passwords 2 | cwe: 307 3 | categories: [attack] 4 | description: The application allows multiple/consecutive attempts at authentication (bruteforce). 5 | mitigations: 6 | - description: Ensure that users are locked out for a certain timeframe/till administrator's intervention when multiple 7 | authentication attempts are encountered. 8 | phase: Architecture and Design 9 | - description: Log invalid access attempts to ensure that potentially malicious bruteforce attempts are logged 10 | phase: Architecture and Design 11 | test-cases: 12 | - name: automated-vulnerability-scanning 13 | test: run automated vulnerability discovery tools and bruteforce against the application 14 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 15 | type: discovery 16 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/layouts/error.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /api/tp_api/swagger/target-create-update.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint to create and update Targets for Vulnerability Scanning and Management 2 | --- 3 | securitySchemes: 4 | bearerAuth: 5 | type: http 6 | scheme: bearer 7 | format: JWT 8 | parameters: 9 | - name: body 10 | in: body 11 | required: true 12 | schema: 13 | id: Target 14 | type: object 15 | required: 16 | - name 17 | - url 18 | - project 19 | properties: 20 | name: 21 | type: string 22 | url: 23 | type: string 24 | project: 25 | type: string 26 | 27 | responses: 28 | 200: 29 | description: Target successfully created 30 | 400: 31 | description: Input Validation Errors or other client Errors 32 | 404: 33 | description: Unable to find Project 34 | 35 | -------------------------------------------------------------------------------- /documentation/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | ``` 30 | $ GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Playbook-Frontend", 3 | "version": "1.0.0", 4 | "description": "My swell Nuxt.js project", 5 | "author": "Tilak T", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "generate": "nuxt generate", 12 | "test": "jest" 13 | }, 14 | "dependencies": { 15 | "@nuxtjs/auth": "^4.9.1", 16 | "@nuxtjs/axios": "^5.3.6", 17 | "apexcharts": "^3.19.0", 18 | "nuxt": "^2.0.0", 19 | "uuid": "^8.0.0", 20 | "vue-apexcharts": "^1.5.3", 21 | "vue-organization-chart": "^1.1.6", 22 | "vuelidate": "^0.7.5" 23 | }, 24 | "devDependencies": { 25 | "@nuxtjs/vuetify": "^1.0.0", 26 | "@vue/test-utils": "^1.0.0-beta.27", 27 | "babel-jest": "^24.1.0", 28 | "jest": "^24.1.0", 29 | "vue-jest": "^4.0.0-0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ThreatPlaybook 2 | 3 | > This is version 3 (beta) 4 | 5 | ### What it was: 6 | A (relatively) Unopinionated framework that faciliates Threat Modeling as Code married with Application Security Automation on a single Fabric 7 | 8 | ### What it is now: 9 | A unified DevSecOps Framework that allows you to go from iterative, collaborative Threat Modeling to Application Security Test Orchestration 10 | 11 | [![Black Hat Arsenal USA](https://rawgit.com/toolswatch/badges/master/arsenal/usa/2018.svg)](https://www.blackhat.com/us-18/arsenal/schedule/index.html#threatplaybook-11697) 12 | 13 | 14 | [Documentation](https://threatplaybook.netlify.app) 15 | 16 | * [Quick Start](https://threatplaybook.netlify.app/docs/installation) 17 | * [Tutorial](https://threatplaybook.netlify.app/docs/tutorial) 18 | 19 | 20 | ![](img/tp-logo.png) 21 | 22 | ## Brought to you proudly by 23 | ![](img/we45logo.jpg) -------------------------------------------------------------------------------- /api/requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.3 2 | attrs==19.3.0 3 | bcrypt==3.1.7 4 | black==19.10b0 5 | blinker==1.4 6 | cffi==1.14.0 7 | click==7.1.1 8 | entrypoints==0.3 9 | flake8==3.7.9 10 | flasgger==0.9.4 11 | Flask==1.1.2 12 | Flask-Cors==3.0.8 13 | flask-mongoengine==0.9.5 14 | Flask-WTF==0.14.3 15 | importlib-metadata==1.6.0 16 | itsdangerous==1.1.0 17 | Jinja2==2.11.2 18 | jsonschema==3.2.0 19 | loguru==0.4.1 20 | MarkupSafe==1.1.1 21 | mccabe==0.6.1 22 | mistune==0.8.4 23 | mongoengine==0.19.1 24 | more-itertools==8.2.0 25 | packaging==20.3 26 | pathspec==0.8.0 27 | pluggy==0.13.1 28 | py==1.8.1 29 | pycodestyle==2.5.0 30 | pycparser==2.20 31 | pyflakes==2.1.1 32 | PyJWT==1.7.1 33 | pymongo==3.10.1 34 | pyparsing==2.4.7 35 | pyrsistent==0.16.0 36 | pytest==5.4.1 37 | pytest-flask==1.0.0 38 | PyYAML==5.3.1 39 | regex==2020.4.4 40 | six==1.14.0 41 | toml==0.10.0 42 | typed-ast==1.4.1 43 | wcwidth==0.1.9 44 | Werkzeug==1.0.1 45 | WTForms==2.3.1 46 | zipp==3.1.0 47 | -------------------------------------------------------------------------------- /documentation/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #25c2a0; 11 | --ifm-color-primary-dark: rgb(33, 175, 144); 12 | --ifm-color-primary-darker: rgb(31, 165, 136); 13 | --ifm-color-primary-darkest: rgb(26, 136, 112); 14 | --ifm-color-primary-light: rgb(70, 203, 174); 15 | --ifm-color-primary-lighter: rgb(102, 212, 189); 16 | --ifm-color-primary-lightest: rgb(146, 224, 208); 17 | --ifm-code-font-size: 95%; 18 | } 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgb(72, 77, 91); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | -------------------------------------------------------------------------------- /api/tp_api/swagger/scenario-repo.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to create and update Threat Scenarios that are linked to existing Vulnerability Repositories 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Threat Scenario 9 | type: object 10 | required: 11 | - name 12 | - abuser_story 13 | - feature 14 | - type 15 | - repo_name 16 | - description 17 | properties: 18 | name: 19 | type: string 20 | description: 21 | type: string 22 | feature: 23 | type: string 24 | abuser_story: 25 | type: string 26 | type: 27 | type: string 28 | repo_name: 29 | type: string 30 | responses: 31 | 200: 32 | description: Threat Scenario successfully created 33 | 400: 34 | description: Input Validation Errors or other client Errors 35 | 404: 36 | description: Unable to find User Story OR Abuser Story OR Repo Item 37 | 38 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/projects/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 45 | -------------------------------------------------------------------------------- /api/tp_api/repo/aws_s3.yaml: -------------------------------------------------------------------------------- 1 | name: Misconfigured AWS S3 Bucket 2 | cwe: 16 3 | description: Misconfigured Amazon AWS S3 Bucket(s) might allow unauthorized users to read/write files in the specific 4 | S3 buckets 5 | mitigations: 6 | - description: Ensure that IAM Policies are set for AWS S3 to protect against public/unauthorized access to unauthorized users 7 | phase: Implementation 8 | - description: Consider implementation of encryption for data stored in AWS S3 9 | phase: Implementation 10 | risk: 11 | - consequence: Unauthorized Users may gain access to files with sensitive information in the AWS S3 bucket(s) 12 | type: Confidentiality 13 | - consequence: Unauthorized Users may write/modify data in the AWS S3 bucket(s) 14 | type: Integrity 15 | test-cases: 16 | - name: automated-vulnerability-scanning 17 | test: run automated vulnerability discovery tools to identify misconfigured AWS S3 buckets 18 | tools: [scout2,prowler,weirdaal,burpsuite] 19 | type: discovery 20 | - name: manual 21 | test: test for misconfigured AWS S3 Buckets manually, as part of a Pentest or Bug-bounty 22 | type: manual 23 | categories: [cloud] 24 | -------------------------------------------------------------------------------- /api/tp_api/swagger/test-case-create.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint to create and update Security Test Cases a.k.a Test Cases 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Security Test Case 9 | type: object 10 | required: 11 | - test_case 12 | - name 13 | - threat_scenario 14 | - tools 15 | - tags 16 | - executed 17 | - test_type 18 | properties: 19 | name: 20 | type: string 21 | test_case: 22 | type: string 23 | threat_scenario: 24 | type: string 25 | tools: 26 | type: array 27 | items: 28 | type: string 29 | tags: 30 | type: array 31 | items: 32 | type: string 33 | executed: 34 | type: boolean 35 | test_type: 36 | type: string 37 | responses: 38 | 200: 39 | description: Test Case successfully created 40 | 400: 41 | description: Input Validation Errors or other client Errors 42 | 404: 43 | description: Unable to find threat scenario 44 | 45 | -------------------------------------------------------------------------------- /api/tp_api/swagger/scenario-inline.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to create and update Inline Threat Scenarios that are NOT linked to existing Vulnerability Repositories 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Threat Scenario 9 | type: object 10 | required: 11 | - name 12 | - abuser_story 13 | - feature 14 | - type 15 | - cwe 16 | - description 17 | - severity 18 | - vul_name 19 | properties: 20 | name: 21 | type: string 22 | description: 23 | type: string 24 | feature: 25 | type: string 26 | abuser_story: 27 | type: string 28 | type: 29 | type: string 30 | vul_name: 31 | type: string 32 | severity: 33 | type: integer 34 | cwe: 35 | type: integer 36 | responses: 37 | 200: 38 | description: Threat Scenario successfully created 39 | 400: 40 | description: Input Validation Errors or other client Errors 41 | 404: 42 | description: Unable to find User Story OR Abuser Story OR Repo Item 43 | 44 | -------------------------------------------------------------------------------- /nginx/sites-enabled/tp_nginx: -------------------------------------------------------------------------------- 1 | upstream frontend_server { 2 | server frontend:80 fail_timeout=1000; 3 | } 4 | 5 | server { 6 | listen 80; 7 | listen 5000; 8 | server_name 127.0.0.1; 9 | client_max_body_size 10G; 10 | 11 | autoindex off; 12 | 13 | gzip on; 14 | gzip_types text/plain text/css application/javascript; 15 | 16 | location /api { 17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 | fastcgi_hide_header X-Powered-By; 19 | proxy_set_header Host $http_host; 20 | proxy_read_timeout 1000s; 21 | proxy_connect_timeout 1000s; 22 | proxy_send_timeout 1000s; 23 | proxy_pass http://api:5000; 24 | } 25 | 26 | location / { 27 | 28 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 29 | 30 | fastcgi_hide_header X-Powered-By; 31 | 32 | proxy_set_header Host $http_host; 33 | proxy_read_timeout 1000s; 34 | proxy_connect_timeout 1000s; 35 | proxy_send_timeout 1000s; 36 | proxy_redirect off; 37 | 38 | if (!-f $request_filename) { 39 | proxy_pass http://frontend_server; 40 | break; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /documentation/docs/create-project.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: create-project 3 | title: Create Project 4 | sidebar_label: Create Project 5 | --- 6 | 7 | ``` 8 | playbook apply project -n test-project 9 | ``` 10 | >**Note:** `test-project` is the name of the project. 11 | 12 | ### Create a single Feature with Threat Model 13 | 14 | * Now you'll be loading a story-driven threat model. It starts with the Feature/User-Story. 15 | * Each Feature/UserStory can have multiple Abuser Stories. 16 | * Each Abuser Story can have multiple Threat Scenarios 17 | * Each Threat Scenario can have multiple Test Cases and Mitigations 18 | 19 | 20 | #### Download example feature 21 | ``` 22 | wget -O feature.yaml https://raw.githubusercontent.com/we45/ThreatPlaybook-ClientV3/master/create-upload.yaml 23 | ``` 24 | 25 | #### Upload the features into `test-project` 26 | ``` 27 | playbook apply feature -f feature.yaml -p test-project 28 | ``` 29 | 30 | And that's it! You've created your first every Threat Model in ThreatPlaybook. 31 | 32 | ##### Of course, this is not all that ThreatPlaybook has to offer. It has: 33 | * A useful WebUI for viewing the Features/UserStories -> Abuser Stories -> Threat Models, Scans and more 34 | * Features for processing and managing Vulnerability Results 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /api/tp_api/repo/ssrf.yaml: -------------------------------------------------------------------------------- 1 | name: Server-Side Request Forgery 2 | cwe: 918 3 | description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. 4 | mitigations: 5 | - description: Consider scanning dependencies for SSRF vulnerabilities, including infrastructure components like Apache 6 | phase: Implementation 7 | - description: Consider implementing a URL whitelist to reduce the possibility of an attacker-controlled URL being resolved by your application 8 | phase: Implementation 9 | - description: Consider implementing additional Header validation to sensitive paths like EC2 Metadata, etc to mitigate the effects of an existing SSRF flaw 10 | phase: Implementation 11 | risk: 12 | - consequence: Unauthorized users may be able to gain access to sensitive information in internal/protected system components 13 | type: Confidentiality 14 | test-cases: 15 | - name: automated-vulnerability-scanning 16 | test: run automated vulnerability discovery tools to identify SSRF 17 | tools: [burpsuite,zap] 18 | type: discovery 19 | - name: manual 20 | test: test for misconfigured AWS S3 Buckets manually, as part of a Pentest or Bug-bounty 21 | type: manual 22 | categories: [cloud] 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx: 4 | image: we45/threatplaybook-nginx:latest 5 | ports: 6 | - "80:80" 7 | depends_on: 8 | - api 9 | links: 10 | - api 11 | mongo_db: 12 | image: 'docker.io/bitnami/mongodb:4.2-debian-10' 13 | volumes: 14 | - './playbook:/bitnami/mongodb' 15 | environment: 16 | - MONGODB_USERNAME=threatplaybook 17 | - MONGODB_PASSWORD=password123 18 | - MONGODB_DATABASE=threat_playbook 19 | expose: 20 | - "27017" 21 | api: 22 | image: we45/threatplaybook-server:latest 23 | expose: 24 | - "5000" 25 | environment: 26 | - MONGO_HOST=mongo_db 27 | - MONGO_USER=threatplaybook 28 | - MONGO_PASS=password123 29 | - MONGO_PORT=27017 30 | - MONGO_DB=threat_playbook 31 | - SUPERUSER_EMAIL=admin@admin.com 32 | - SUPERUSER_PASS=supersecret 33 | - JWT_PASS=VGCxqDnhsN6vNQVqmXtrNVVe1AS36ZMQKTq6lYpj0ygHiuWunMOkFi2j17cHSbG-WId9x_yJpeSqy0TTFjs06Q 34 | - WAIT_HOSTS=mongo_db:27017 35 | links: 36 | - mongo_db 37 | depends_on: 38 | - mongo_db 39 | command: sh -c "/app/wait-for mongo_db:27017 -- /usr/local/bin/python /app/app.py" 40 | frontend: 41 | image: we45/threatplaybook-frontend:latest 42 | environment: 43 | - VUE_APP_API_URL=http://api 44 | links: 45 | - api 46 | depends_on: 47 | - api -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/abuserStory.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | projectAbuserStoryTree: [] 7 | }); 8 | export const mutations = { 9 | IS_PAGE_LOADING(state, data) { 10 | state.isLoading = data; 11 | }, 12 | FETCH_PROJECT_ABUSER_STORY_TREE(state, data) { 13 | state.projectAbuserStoryTree = data; 14 | } 15 | }; 16 | export const actions = { 17 | showPageLoading({ commit }, data) { 18 | commit("IS_PAGE_LOADING", data); 19 | }, 20 | fetchAbuserStoryTreeByProject({ commit }, payload) { 21 | axios 22 | .post(loginUrl+"/api/abuser-story/project", payload, { 23 | headers: { 24 | Authorization: localStorage.getItem("token") 25 | } 26 | }) 27 | .then(res => { 28 | if (res.data.success) { 29 | commit("FETCH_PROJECT_ABUSER_STORY_TREE", res.data.data); 30 | } 31 | commit("IS_FETCHING", false); 32 | }) 33 | .catch(error => { 34 | commit("IS_FETCHING", false); 35 | if (error.response.status === 401) { 36 | } 37 | }); 38 | } 39 | }; 40 | export const getters = { 41 | isPageLoading(state) { 42 | return state.isLoading; 43 | }, 44 | getAbuserStoryProjectTree(state) { 45 | if (state.projectAbuserStoryTree) { 46 | return state.projectAbuserStoryTree; 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/threatmap.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | threatMapDataProject: [] 7 | }); 8 | export const mutations = { 9 | IS_PAGE_LOADING(state, data) { 10 | state.isLoading = data; 11 | }, 12 | FETCH_THREAT_MAP_PROJECT(state, data) { 13 | state.threatMapDataProject = data; 14 | } 15 | }; 16 | export const actions = { 17 | showPageLoading({ commit }, data) { 18 | commit("IS_PAGE_LOADING", data); 19 | }, 20 | fetchThreatMapbyProject({ commit }, payload) { 21 | axios 22 | .post("/api/threatmap/project", payload, { 23 | headers: { 24 | Authorization: localStorage.getItem("token") 25 | } 26 | }) 27 | .then(response => { 28 | if (response.data.success) { 29 | commit("FETCH_THREAT_MAP_PROJECT", response.data.data); 30 | commit("IS_PAGE_LOADING", false); 31 | } 32 | commit("PAGE_LOADING", false); 33 | }) 34 | .catch(error => { 35 | if (error.response.status === 401) { 36 | commit("PAGE_LOADING", false); 37 | } 38 | }); 39 | } 40 | }; 41 | export const getters = { 42 | isPageLoading(state) { 43 | return state.isLoading; 44 | }, 45 | getThreatMapProjectData(state) { 46 | if (state.threatMapDataProject) { 47 | return state.threatMapDataProject; 48 | } 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /api/tp_api/swagger/vulnerability-create.yml: -------------------------------------------------------------------------------- 1 | This is the API Endpoint that is used to create a scan for storing Vulnerability Results in the Database 2 | --- 3 | parameters: 4 | - name: body 5 | in: body 6 | required: true 7 | schema: 8 | id: Vulnerability 9 | type: object 10 | required: 11 | - scan 12 | - name 13 | - vul_name 14 | - cwe 15 | properties: 16 | scan: 17 | type: string 18 | name: 19 | type: string 20 | description: 21 | type: string 22 | remediation: 23 | type: string 24 | observation: 25 | type: string 26 | vul_name: 27 | type: string 28 | cwe: 29 | type: integer 30 | evidences: 31 | type: array 32 | items: 33 | type: object 34 | properties: 35 | log: 36 | type: string 37 | url: 38 | type: string 39 | param: 40 | type: string 41 | line_num: 42 | type: integer 43 | attack: 44 | type: string 45 | info: 46 | type: string 47 | 48 | responses: 49 | 200: 50 | description: Vulnerability successfully created 51 | 400: 52 | description: Input Validation Errors or other client Errors 53 | 404: 54 | description: Unable to find Scan or Target 55 | 56 | -------------------------------------------------------------------------------- /api/tp_api/repo/cert_validation.yaml: -------------------------------------------------------------------------------- 1 | name: Improper Certificate Validation 2 | description: The software does not validate, or incorrectly validates, a certificate. 3 | cwe: 295 4 | categories: [app_vulns,owasp] 5 | mitigations: 6 | - description: Certificates should be carefully managed and checked to assure that 7 | data are encrypted with the intended owner's public key. 8 | phase: Architecture and Design 9 | - description: Certificates should be deployed with strong CipherSuite Specs and Perfect Forward Secrecy for highest 10 | levels of protection 11 | phase: Implementation 12 | risk: 13 | - consequence: Attacker may be able to leverage a weak SSL Implementation to compromise the master key/keying materials, 14 | thereby compromising the transmission of sensitive information 15 | type: Confidentiality 16 | test-cases: 17 | - name: ssl-test 18 | test: Check for SSL/TLS implementation with automated tools 19 | tools: [ssllabs.com,testssl.sh,sslyze,sslscan,nmap] 20 | - name: source-composition-scanning 21 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of misconfigured SSL/TLS Implementation or Plaintext Data Transmission 22 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 23 | type: sca 24 | - name: static-analysis 25 | test: Run Static Analysis tools to identify instances that relate to misconfigured SSL/TLS Implementation or Plaintext Data Transmission 26 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity] 27 | type: sast -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/projects.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | data: [] 7 | }); 8 | export const mutations = { 9 | IS_PAGE_LOADING(state, data) { 10 | state.isLoading = data; 11 | }, 12 | FETCH_DATA(state, data) { 13 | state.data = data; 14 | } 15 | }; 16 | export const actions = { 17 | showPageLoading({ commit }, data) { 18 | commit("IS_PAGE_LOADING", data); 19 | }, 20 | fetchProjectData({ commit }) { 21 | axios 22 | .get("/api/project/read", { 23 | headers: { 24 | Authorization: localStorage.getItem("token") 25 | } 26 | }) 27 | .then(response => { 28 | if (response.data.success) { 29 | const projectData = []; 30 | for (const data of response.data.data) { 31 | projectData.push({ name: data.name }); 32 | } 33 | commit("FETCH_DATA", projectData); 34 | commit("IS_PAGE_LOADING", false); 35 | } 36 | commit("PAGE_LOADING", false); 37 | }) 38 | .catch(error => { 39 | if (error.response.status === 401) { 40 | commit("PAGE_LOADING", false); 41 | } 42 | }); 43 | } 44 | }; 45 | export const getters = { 46 | isPageLoading(state) { 47 | return state.isLoading; 48 | }, 49 | getProjectCount(state) { 50 | if (state.data) { 51 | return state.data.length; 52 | } 53 | }, 54 | getProjectData(state) { 55 | if (state.data) { 56 | return state.data; 57 | } 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | -------------------------------------------------------------------------------- /api/tp_api/wait-for: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TIMEOUT=90 4 | QUIET=0 5 | 6 | echoerr() { 7 | if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi 8 | } 9 | 10 | usage() { 11 | exitcode="$1" 12 | cat << USAGE >&2 13 | Usage: 14 | $cmdname host:port [-t timeout] [-- command args] 15 | -q | --quiet Do not output any status messages 16 | -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout 17 | -- COMMAND ARGS Execute command with args after the test finishes 18 | USAGE 19 | exit "$exitcode" 20 | } 21 | 22 | wait_for() { 23 | for i in `seq $TIMEOUT` ; do 24 | nc -z "$HOST" "$PORT" > /dev/null 2>&1 25 | 26 | result=$? 27 | if [ $result -eq 0 ] ; then 28 | if [ $# -gt 0 ] ; then 29 | exec "$@" 30 | fi 31 | exit 0 32 | fi 33 | sleep 1 34 | done 35 | echo "Operation timed out" >&2 36 | exit 1 37 | } 38 | 39 | while [ $# -gt 0 ] 40 | do 41 | case "$1" in 42 | *:* ) 43 | HOST=$(printf "%s\n" "$1"| cut -d : -f 1) 44 | PORT=$(printf "%s\n" "$1"| cut -d : -f 2) 45 | shift 1 46 | ;; 47 | -q | --quiet) 48 | QUIET=1 49 | shift 1 50 | ;; 51 | -t) 52 | TIMEOUT="$2" 53 | if [ "$TIMEOUT" = "" ]; then break; fi 54 | shift 2 55 | ;; 56 | --timeout=*) 57 | TIMEOUT="${1#*=}" 58 | shift 1 59 | ;; 60 | --) 61 | shift 62 | break 63 | ;; 64 | --help) 65 | usage 0 66 | ;; 67 | *) 68 | echoerr "Unknown argument: $1" 69 | usage 1 70 | ;; 71 | esac 72 | done 73 | 74 | if [ "$HOST" = "" -o "$PORT" = "" ]; then 75 | echoerr "Error: you need to provide a host and port to test." 76 | usage 2 77 | fi 78 | 79 | wait_for "$@" 80 | -------------------------------------------------------------------------------- /documentation/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: "Threat Playbook", 3 | tagline: 4 | "A (relatively) Unopinionated framework that faciliates Threat Modeling as Code married with Application Security Automation on a single Fabric", 5 | url: "https://your-docusaurus-test-site.com", 6 | baseUrl: "/", 7 | favicon: "img/favicon.ico", 8 | organizationName: "we45", 9 | projectName: "ThreatPlaybook", 10 | themeConfig: { 11 | navbar: { 12 | logo: { 13 | alt: "Logo", 14 | src: "img/logo.png", 15 | }, 16 | links: [ 17 | { 18 | to: "docs", 19 | activeBasePath: "/", 20 | label: "Documentation", 21 | position: "left", 22 | }, 23 | ], 24 | }, 25 | footer: { 26 | style: "dark", 27 | links: [ 28 | { 29 | title: "Community", 30 | items: [ 31 | { 32 | label: "Twitter", 33 | href: "https://twitter.com/we45", 34 | }, 35 | ], 36 | }, 37 | { 38 | title: "More", 39 | items: [ 40 | { 41 | label: "GitHub", 42 | href: "https://github.com/we45/ThreatPlaybook", 43 | }, 44 | ], 45 | }, 46 | ], 47 | copyright: `Copyright © ${new Date().getFullYear()} we45, Inc. Built with Docusaurus.`, 48 | }, 49 | }, 50 | presets: [ 51 | [ 52 | "@docusaurus/preset-classic", 53 | { 54 | docs: { 55 | homePageId: "overview", 56 | sidebarPath: require.resolve("./sidebars.js"), 57 | }, 58 | theme: { 59 | customCss: require.resolve("./src/css/custom.css"), 60 | }, 61 | }, 62 | ], 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 80 | -------------------------------------------------------------------------------- /api/tp_api/repo/plaintext-transmission.yaml: -------------------------------------------------------------------------------- 1 | name: Plaintext transmission of sensitive data 2 | cwe: 319 3 | categories: [app_vulns,owasp] 4 | description: The software transmits sensitive or security-critical data in cleartext 5 | in a communication channel that can be sniffed by unauthorized actors. 6 | mitigations: 7 | - description: Encrypt the data with a reliable encryption scheme before transmitting. 8 | phase: Architecture and Design 9 | - description: When using web applications with SSL, use SSL for the entire session 10 | from login to logout, not just for the initial login page. 11 | phase: Implementation 12 | - description: Use tools and techniques that require manual (human) analysis, such 13 | as penetration testing, threat modeling, and interactive tools that allow the 14 | tester to record and modify an active session. These may be more effective than 15 | strictly automated techniques. This is especially the case with weaknesses that 16 | are related to design and business rules. 17 | phase: Testing 18 | - description: Configure servers to use encrypted channels for communication, which 19 | may include SSL or other secure protocols. 20 | phase: Operation 21 | risk: 22 | - consequence: Anyone can read the information by gaining access to the channel being 23 | used for communication. 24 | type: Confidentiality 25 | test-cases: 26 | - name: ssl-test 27 | test: Check for SSL/TLS implementation with automated tools 28 | tools: [ssllabs.com,testssl.sh,sslyze,sslscan,nmap] 29 | type: discovery 30 | - name: source-composition-scanning 31 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of misconfigured SSL/TLS Implementation or Plaintext Data Transmission 32 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 33 | type: sca 34 | - name: static-analysis 35 | test: Run Static Analysis tools to identify instances that relate to misconfigured SSL/TLS Implementation or Plaintext Data Transmission 36 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity] 37 | type: sast 38 | related_cwes: 39 | - 311 40 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/components/project/Content.vue: -------------------------------------------------------------------------------- 1 | 51 | 79 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/home.js: -------------------------------------------------------------------------------- 1 | export const state = () => ({ 2 | isLoading: false, 3 | data: [], 4 | projectCount: 0, 5 | userStoriesCount: 0, 6 | threatScenarioCount: 0, 7 | scanCount: 0, 8 | threatScenarioSevChart: [], 9 | severityChart: [] 10 | }) 11 | export const mutations = { 12 | IS_PAGE_LOADING(state, data) { 13 | state.isLoading = data 14 | }, 15 | FETCH_DATA(state, data) { 16 | state.data = data 17 | }, 18 | FETCH_PROJECT_COUNT(state, data) { 19 | state.projectCount = data 20 | }, 21 | FETCH_USER_STORIES_COUNT(state, data) { 22 | state.userStoriesCount = data 23 | }, 24 | FETCH_THREAT_SCENARIO_COUNT(state, data) { 25 | state.threatScenarioCount = data 26 | }, 27 | FETCH_SCAN_COUNT(state, data) { 28 | state.scanCount = data 29 | }, 30 | FETCH_THREAT_SCENARIO_SEVERITY_CHART(state, data) { 31 | state.threatScenarioSevChart = data 32 | }, 33 | FETCH_SEVERITY_CHART(state, data) { 34 | state.severityChart = data 35 | } 36 | } 37 | export const actions = { 38 | showPageLoading({ commit }, data) { 39 | commit('IS_PAGE_LOADING', data) 40 | }, 41 | fetchData({ commit }) { 42 | commit("IS_PAGE_LOADING", false); 43 | } 44 | } 45 | export const getters = { 46 | isPageLoading(state) { 47 | return state.isLoading 48 | }, 49 | getProjectCount(state) { 50 | if (state.projectCount) { 51 | return state.projectCount 52 | } 53 | }, 54 | getUserStoriesCount(state) { 55 | if (state.userStoriesCount) { 56 | return state.userStoriesCount 57 | } 58 | }, 59 | getThreatScenarioCount(state) { 60 | if (state.threatScenarioCount) { 61 | return state.threatScenarioCount 62 | } 63 | }, 64 | getScanCount(state) { 65 | if (state.scanCount) { 66 | return state.scanCount 67 | } 68 | }, 69 | getCountList(state) { 70 | if (state.data) { 71 | return state.data 72 | } 73 | }, 74 | fetchThreatScenarioSevChartData(state) { 75 | if (state.threatScenarioSevChart) { 76 | return state.threatScenarioSevChart 77 | } 78 | }, 79 | fetchSeverityChartData(state) { 80 | if (state.severityChart) { 81 | return state.severityChart 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /api/tp_api/repo/xxe.yaml: -------------------------------------------------------------------------------- 1 | name: XML External Entities 2 | cwe: 611 3 | description: The software processes an XML document that can contain XML entities 4 | with URIs that resolve to documents outside of the intended sphere of control, causing 5 | the product to embed incorrect documents into its output. 6 | categories: [app_vulns,owasp] 7 | mitigations: 8 | - description: XML parsers and validators must be configured to disable external 9 | entity expansion, general entities and parameter entity resolution 10 | phase: Implementation 11 | risk: 12 | - consequence: If the attacker is able to include a crafted DTD and a default entity 13 | resolver is enabled, the attacker may be able to access arbitrary files on the 14 | system. 15 | type: Confidentiality 16 | - consequence: The DTD may include arbitrary HTTP requests that the server may execute. 17 | This could lead to other attacks leveraging the server's trust relationship with 18 | other entities. 19 | type: Integrity 20 | - consequence: The software could consume excessive CPU cycles or memory using a URI 21 | that points to a large file, or a device that always returns data such as /dev/random. 22 | Alternately, the URI could reference a file that contains many nested or recursive 23 | entity references to further slow down parsing. 24 | type: Availability 25 | test-cases: 26 | - name: automated-vulnerability-scanning 27 | test: run automated vulnerability discovery tools and its XXE payloads against the application 28 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 29 | type: discovery 30 | - name: manual 31 | test: test for XXE variants manually with pentesters, bug-bounty 32 | type: manual 33 | - name: source-composition-scanning 34 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of XXE 35 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 36 | type: sca 37 | - name: static-analysis 38 | test: Run Static Analysis tools to identify instances of dynamic queries or stored procedures that are vulnerable to XXE 39 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity] 40 | type: sast -------------------------------------------------------------------------------- /api/tp_api/repo/idor_pk.yaml: -------------------------------------------------------------------------------- 1 | name: Insecure Direct Object Reference - Primary Key 2 | cwe: 639 3 | description: The system's authorization functionality does not prevent one user from 4 | gaining access to another user's data or record by modifying the key value identifying 5 | the data. 6 | mitigations: 7 | - description: For each and every data access, ensure that the user has sufficient 8 | privilege to access the record that is being requested. 9 | phase: Architecture and Design 10 | - description: Make sure that the key that is used in the lookup of a specific user's 11 | record is not controllable externally by the user or that any tampering can be 12 | detected. 13 | phase: Architecture and Design 14 | - description: Use encryption in order to make it more difficult to guess other legitimate 15 | values of the key or associate a digital signature with the key so that the server 16 | can verify that there has been no tampering. 17 | phase: Architecture and Design 18 | risk: 19 | - consequence: Access control checks for specific user data or functionality can be 20 | bypassed. 21 | type: Access_Control 22 | - consequence: Horizontal escalation of privilege is possible (one user can view/modify 23 | information of another user). 24 | type: Access_Control 25 | - consequence: Vertical escalation of privilege is possible if the user-controlled 26 | key is actually a flag that indicates administrator status, allowing the attacker 27 | to gain administrative access. 28 | type: Access_Control 29 | categories: [app_vulns] 30 | test-cases: 31 | - name: manual 32 | test: test for Insecure Direct Object References variants manually with pentesters, bug-bounty 33 | type: manual 34 | - name: fuzzing 35 | test: test for IDORs with Fuzzing with Automated tools 36 | tools: [burpsuite,owasp-zap,wfuzz,mitmproxy] 37 | type: automated 38 | - name: static-analysis 39 | test: Run Static Analysis tools to identify instances of improper input validation and output encoding for Insecure Deserialization 40 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity] 41 | type: sast 42 | - name: source-composition-scanning 43 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of Insecure Deserialization 44 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 45 | type: sca 46 | related_cwes: 47 | - 284 48 | - 285 49 | 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | example/*.html 2 | example/*.xml 3 | example/*.json 4 | example/*.png 5 | example/*.txt 6 | example/*.mmd 7 | example/*.md 8 | results/ 9 | tv/ 10 | ThreatPlaybook.egg-info/* 11 | dist/* 12 | threat_playbook/ThreatPlaybook.egg-info/dependency_links.txt 13 | threat_playbook/ThreatPlaybook.egg-info/entry_points.txt 14 | threat_playbook/ThreatPlaybook.egg-info/PKG-INFO 15 | threat_playbook/ThreatPlaybook.egg-info/requires.txt 16 | threat_playbook/ThreatPlaybook.egg-info/SOURCES.txt 17 | threat_playbook/ThreatPlaybook.egg-info/top_level.txt 18 | venv3/* 19 | v1/ThreatPlaybook.egg-info/top_level.txt 20 | v1/ThreatPlaybook.egg-info/SOURCES.txt 21 | v1/ThreatPlaybook.egg-info/requires.txt 22 | v1/ThreatPlaybook.egg-info/PKG-INFO 23 | v1/ThreatPlaybook.egg-info/entry_points.txt 24 | v1/ThreatPlaybook.egg-info/dependency_links.txt 25 | v3/cli/.cred 26 | v3/cli/schematest.json 27 | v3/cli/schema.schema 28 | v3/cli/threat_model_example.json 29 | v3/cli/case_example.json 30 | v3/cli/repo_gql.json 31 | v3/cli/feature_test.json 32 | v3/api/.env 33 | .idea/encodings.xml 34 | *.xml 35 | *.xml 36 | .idea/vcs.xml 37 | .idea/ThreatPlaybook.iml 38 | v3/api/.env 39 | v3/api/.env 40 | .idea/vcs.xml 41 | .idea/* 42 | v3/threatplaybook_db/ 43 | v3/robot/test/log.html 44 | v3/robot/test/report.html 45 | v3/.DS_Store 46 | v3/frontend/.DS_Store 47 | .DS_Store 48 | api/.env 49 | threatplaybook_db/* 50 | robot/test/log.html 51 | robot/test/report.html 52 | robot/test/log.html 53 | robot/test/report.html 54 | robot/test/log.html 55 | *.html 56 | v3/frontend/ThreatPlaybook-Frontend/node_modules/ 57 | v3/frontend/ThreatPlaybook-Frontend/dist/ 58 | v3/frontend/ThreatPlaybook-Frontend/npm-debug.log* 59 | v3/frontend/ThreatPlaybook-Frontend/yarn-debug.log* 60 | v3/frontend/ThreatPlaybook-Frontend/yarn-error.log* 61 | v3/frontend/ThreatPlaybook-Frontend/package-lock.json 62 | frontend/ThreatPlaybook-Frontend/node_modules/ 63 | frontend/ThreatPlaybook-Frontend/npm-debug.log* 64 | frontend/ThreatPlaybook-Frontend/yarn-debug.log* 65 | frontend/ThreatPlaybook-Frontend/yarn-error.log* 66 | frontend/ThreatPlaybook-Frontend/package-lock.json 67 | # frontend/ThreatPlaybook-Frontend/.babelrc 68 | # frontend/ThreatPlaybook-Frontend/.editorconfig 69 | Pipfile.lock 70 | Pipfile 71 | cli/dist/ThreatPlaybook-Client-3.0.3.tar.gz 72 | cli/playbook/.cred 73 | venv/ 74 | *.pyc 75 | api/__pycache__/utils.cpython-37.pyc 76 | api/__pycache__/models.cpython-37.pyc 77 | .vscode/settings.json 78 | *.lock 79 | apiv2/.vscode/settings.json 80 | apiv2/tp_api/logs/output.log 81 | apiv2/logs/output.log 82 | logs/output.log 83 | website/node_modules/* 84 | frontend/Playbook-Frontend/package-lock.json 85 | api/tp_api/logs/output.log 86 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/nuxt.config.js: -------------------------------------------------------------------------------- 1 | import colors from "vuetify/es5/util/colors"; 2 | export default { 3 | mode: "universal", 4 | /* 5 | ** Headers of the page 6 | */ 7 | head: { 8 | titleTemplate: "%s - " + process.env.npm_package_name, 9 | title: process.env.npm_package_name || "", 10 | meta: [ 11 | { charset: "utf-8" }, 12 | { name: "viewport", content: "width=device-width, initial-scale=1" }, 13 | { 14 | hid: "description", 15 | name: "description", 16 | content: process.env.npm_package_description || "" 17 | } 18 | ], 19 | link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }] 20 | }, 21 | /* 22 | ** Customize the progress-bar color 23 | */ 24 | env: { 25 | VUE_APP_API_URL: process.env.VUE_APP_API_URL 26 | }, 27 | 28 | loading: { color: "#fff" }, 29 | /* 30 | ** Global CSS 31 | */ 32 | css: [], 33 | /* 34 | ** Plugins to load before mounting the App 35 | */ 36 | plugins: [ 37 | { src: "~plugins/vue-apexchart.js", ssr: false }, 38 | { src: "~plugins/vue-organization-chart.js", ssr: false } 39 | ], 40 | /* 41 | ** Nuxt.js dev-modules 42 | */ 43 | buildModules: ["@nuxtjs/vuetify"], 44 | /* 45 | ** Nuxt.js modules 46 | */ 47 | modules: [ 48 | // Doc: https://axios.nuxtjs.org/usage 49 | "@nuxtjs/axios", 50 | "@nuxtjs/auth" 51 | ], 52 | /* 53 | ** Axios module configuration 54 | ** See https://axios.nuxtjs.org/options 55 | */ 56 | axios: { 57 | baseURL: process.env.VUE_APP_API_URL 58 | }, 59 | 60 | 61 | /* 62 | ** vuetify module configuration 63 | ** https://github.com/nuxt-community/vuetify-module 64 | */ 65 | vuetify: { 66 | customVariables: ["~/assets/variables.scss"], 67 | theme: { 68 | // disable: true, 69 | dark: false, 70 | themes: { 71 | dark: { 72 | primary: colors.blue.darken2, 73 | accent: colors.grey.darken3, 74 | secondary: colors.amber.darken3, 75 | info: colors.teal.lighten1, 76 | warning: colors.amber.base, 77 | error: colors.deepOrange.accent4, 78 | success: colors.green.accent3 79 | } 80 | } 81 | } 82 | }, 83 | /* 84 | ** Build configuration 85 | */ 86 | build: { 87 | vendor: ["vue-apexchart"], 88 | /* 89 | ** You can extend webpack config here 90 | */ 91 | extend(config, ctx) { 92 | const vueLoader = config.module.rules.find( 93 | rule => rule.loader === "vue-loader" 94 | ); 95 | vueLoader.options.transformToRequire = { 96 | img: "src", 97 | image: "xlink:href", 98 | "b-img": "src", 99 | "b-img-lazy": ["src", "blank-src"], 100 | "b-card": "img-src", 101 | "b-card-img": "img-src", 102 | "b-carousel-slide": "img-src", 103 | "b-embed": "src" 104 | }; 105 | } 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/login.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | token: "", 7 | errorMessage: "", 8 | isError: false 9 | }); 10 | 11 | export const mutations = { 12 | PAGE_LOADING(state, data) { 13 | state.isLoading = data; 14 | }, 15 | STORE_TOKEN(state, data) { 16 | state.token = data; 17 | localStorage.setItem("token", data); 18 | }, 19 | ERROR_MESSAGE(state, data) { 20 | state.errorMessage = data; 21 | }, 22 | ERROR_MESSAGE_STATUS(state, data) { 23 | state.isError = data; 24 | } 25 | }; 26 | 27 | export const actions = { 28 | pageLoadingError({ commit }, data) { 29 | commit("ERROR_MESSAGE_STATUS", data); 30 | }, 31 | pageLoading({ commit }, data) { 32 | commit("PAGE_LOADING", data); 33 | }, 34 | loginUser({ commit }, data) { 35 | axios 36 | .post("/api/login", data) 37 | .then(response => { 38 | if (response.data.success) { 39 | commit("STORE_TOKEN", response.data.data.token); 40 | commit("PAGE_LOADING", false); 41 | this.$router.push("/home"); 42 | } else { 43 | localStorage.removeItem("token"); 44 | commit("PAGE_LOADING", false); 45 | commit("ERROR_MESSAGE_STATUS", true); 46 | this.$router.push("/"); 47 | } 48 | commit("PAGE_LOADING", false); 49 | }) 50 | .catch(error => { 51 | if (error.response.status === 401) { 52 | commit("PAGE_LOADING", false); 53 | commit("ERROR_MESSAGE", "Invalid credentials"); 54 | commit("ERROR_MESSAGE_STATUS", true); 55 | } 56 | if (error.response.status === 502) { 57 | commit("PAGE_LOADING", false); 58 | commit("ERROR_MESSAGE", "Bad Gateway"); 59 | commit("ERROR_MESSAGE_STATUS", true); 60 | } 61 | if (error.response.status === 404) { 62 | commit("PAGE_LOADING", false); 63 | commit("ERROR_MESSAGE", "Page Not found"); 64 | commit("ERROR_MESSAGE_STATUS", true); 65 | } 66 | if (error.response.status === 403) { 67 | commit("PAGE_LOADING", false); 68 | commit("ERROR_MESSAGE", "Invalid credentials"); 69 | commit("ERROR_MESSAGE_STATUS", true); 70 | } 71 | commit("PAGE_LOADING", false); 72 | commit("ERROR_MESSAGE_STATUS", true); 73 | localStorage.removeItem("token"); 74 | this.$router.push("/"); 75 | }); 76 | } 77 | }; 78 | export const getters = { 79 | isPageLoading(state) { 80 | if (state.isLoading) { 81 | return state.isLoading; 82 | } 83 | }, 84 | userToken(state) { 85 | if (state.token) { 86 | return state.token; 87 | } 88 | }, 89 | loginErrorMessage(state) { 90 | if (state.errorMessage) { 91 | return state.errorMessage; 92 | } 93 | }, 94 | isLoginError(state) { 95 | if (state.isError) { 96 | return state.isError; 97 | } 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /documentation/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import useBaseUrl from '@docusaurus/useBaseUrl'; 7 | import styles from './styles.module.css'; 8 | 9 | const features = [ 10 | { 11 | title: <>Easy to Use, 12 | imageUrl: 'img/undraw_docusaurus_mountain.svg', 13 | description: ( 14 | <> 15 | Docusaurus was designed from the ground up to be easily installed and 16 | used to get your website up and running quickly. 17 | 18 | ), 19 | }, 20 | { 21 | title: <>Focus on What Matters, 22 | imageUrl: 'img/undraw_docusaurus_tree.svg', 23 | description: ( 24 | <> 25 | Docusaurus lets you focus on your docs, and we'll do the chores. Go 26 | ahead and move your docs into the docs directory. 27 | 28 | ), 29 | }, 30 | { 31 | title: <>Powered by React, 32 | imageUrl: 'img/undraw_docusaurus_react.svg', 33 | description: ( 34 | <> 35 | Extend or customize your website layout by reusing React. Docusaurus can 36 | be extended while reusing the same header and footer. 37 | 38 | ), 39 | }, 40 | ]; 41 | 42 | function Feature({imageUrl, title, description}) { 43 | const imgUrl = useBaseUrl(imageUrl); 44 | return ( 45 |
46 | {imgUrl && ( 47 |
48 | {title} 49 |
50 | )} 51 |

{title}

52 |

{description}

53 |
54 | ); 55 | } 56 | 57 | function Home() { 58 | const context = useDocusaurusContext(); 59 | const {siteConfig = {}} = context; 60 | return ( 61 | 64 |
65 |
66 |

{siteConfig.title}

67 |

{siteConfig.tagline}

68 |
69 | 75 | Get Started 76 | 77 |
78 |
79 |
80 | {/*
81 | {features && features.length > 0 && ( 82 |
83 |
84 |
85 | {features.map((props, idx) => ( 86 | 87 | ))} 88 |
89 |
90 |
91 | )} 92 |
*/} 93 |
94 | ); 95 | } 96 | 97 | export default Home; 98 | -------------------------------------------------------------------------------- /api/tp_api/repo/insecure_deserialization.yaml: -------------------------------------------------------------------------------- 1 | name: Insecure Deserialization 2 | description: The application deserializes untrusted data without sufficiently verifying 3 | that the resulting data will be valid. 4 | cwe: 502 5 | mitigations: 6 | - description: If available, use the signing/sealing features of the programming language 7 | to assure that deserialized data has not been tainted. For example, a hash-based 8 | message authentication code (HMAC) could be used to ensure that data has not been 9 | modified. 10 | phase: Architecture and Design 11 | - description: When deserializing data, populate a new object rather than just deserializing. 12 | The result is that the data flows through safe input validation and that the functions 13 | are safe. 14 | phase: Implementation 15 | - description: An attempt to serialize and then deserialize a class containing transient 16 | fields will result in NULLs where the transient data should be. This is an excellent 17 | way to prevent time, environment-based, or sensitive variables from being carried 18 | over and used improperly. 19 | phase: Architecture and Design 20 | - description: Avoid having unnecessary types or gadgets available that can be leveraged 21 | for malicious ends. This limits the potential for unintended or unauthorized types 22 | and gadgets to be leveraged by the attacker. Whitelist acceptable classes. Note- 23 | new gadgets are constantly being discovered, so this alone is not a sufficient 24 | mitigation. 25 | phase: Implementation 26 | risk: 27 | - consequence: The consequences can vary widely, because it depends on which objects 28 | or methods are being deserialized, and how they are used. 29 | - consequence: Attackers can modify unexpected objects or data that was assumed to 30 | be safe from modification. 31 | type: Integrity 32 | - consequence: If a function is making an assumption on when to terminate, based on 33 | a sentry in a string, it could easily never terminate. 34 | type: Availability 35 | - consequence: Code could potentially make the assumption that information in the 36 | deserialized object is valid. Functions that make this dangerous assumption could 37 | be exploited. 38 | type: Authorization 39 | categories: [app_vulns,owasp] 40 | test-cases: 41 | - name: automated-vulnerability-scanning 42 | test: run automated vulnerability discovery tools and its Insecure Deserialization payloads against the application 43 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 44 | type: discovery 45 | - name: manual 46 | test: test for Insecure Deserialization variants manually with pentesters, bug-bounty 47 | type: manual 48 | - name: static-analysis 49 | test: Run Static Analysis tools to identify instances of improper input validation and output encoding for Insecure Deserialization 50 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity,] 51 | type: sast 52 | - name: source-composition-scanning 53 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of Insecure Deserialization 54 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 55 | type: sca -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/projects/_name/vulnerabilities.vue: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | 69 | 104 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/scan/_name.vue: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | 70 | 106 | -------------------------------------------------------------------------------- /documentation/docs/tutorial.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: tutorial 3 | title: Tutorial - Installation, Configuration and Extension 4 | sidebar_label: Installation and Configuration 5 | --- 6 | 7 | ##### Download docker-compose file 8 | ``` 9 | git clone https://github.com/we45/ThreatPlaybook.git 10 | ``` 11 | 12 | ##### `cd` into the downloaded directory 13 | ``` 14 | cd ThreatPlaybook 15 | ``` 16 | 17 | The ThreatPlaybook `docker-compose.yml` file has been written to get you up and running quickly. However, its not the best/most ideal configuration for running ThreatPlaybook, especially in prod. 18 | 19 | > Its meant only for experimental deployments on user's local machine 20 | 21 | ### Analyzing the Docker-Compose File 22 | 23 | ```yml 24 | version: '3' 25 | services: 26 | nginx: 27 | image: we45/threatplaybook-nginx:latest 28 | ports: 29 | - "80:80" 30 | depends_on: 31 | - api 32 | links: 33 | - api 34 | mongo_db: 35 | image: 'docker.io/bitnami/mongodb:4.2-debian-10' 36 | volumes: 37 | - '/tmp/playbook:/bitnami/mongodb' 38 | environment: 39 | - MONGODB_USERNAME=threatplaybook 40 | - MONGODB_PASSWORD=password123 41 | - MONGODB_DATABASE=threat_playbook 42 | expose: 43 | - "27017" 44 | api: 45 | image: we45/threatplaybook-server:latest 46 | expose: 47 | - "5000" 48 | environment: 49 | - MONGO_HOST=mongo_db 50 | - MONGO_USER=threatplaybook 51 | - MONGO_PASS=password123 52 | - MONGO_PORT=27017 53 | - MONGO_DB=threat_playbook 54 | - SUPERUSER_EMAIL=admin@admin.com 55 | - SUPERUSER_PASS=supersecret 56 | - JWT_PASS=VGCxqDnhsN6vNQVqmXtrNVVe1AS36ZMQKTq6lYpj0ygHiuWunMOkFi2j17cHSbG-WId9x_yJpeSqy0TTFjs06Q 57 | - WAIT_HOSTS=mongo_db:27017 58 | links: 59 | - mongo_db 60 | depends_on: 61 | - mongo_db 62 | command: sh -c "/app/wait-for mongo_db:27017 -- /usr/local/bin/python /app/app.py" 63 | frontend: 64 | image: we45/threatplaybook-frontend:latest 65 | environment: 66 | - VUE_APP_API_URL=http://api 67 | links: 68 | - api 69 | depends_on: 70 | - api 71 | ``` 72 | 73 | ### Change the Default Password! 74 | 75 | * The value set in the `SUPERUSER_EMAIL` and `SUPERUSER_PASS` are the values that ThreatPlaybook uses as the default values for the Superuser Account within ThreatPlaybook. 76 | 77 | > This is clearly a default password that you SHOULD NOT use as a permanent password to access the super-admin account on ThreatPlaybook. 78 | > 79 | > Please use `playbook change-password` feature in the CLI to change your default password. You have been warned! 80 | 81 | ### Logs 82 | 83 | There's a single output log that is generated in the `/app` directory within the container. If you want the logs to persist on a more permanent file-system, you'll need to volume mount a src path on your machine to the `/app/logs` path within the container. 84 | 85 | ``` 86 | # inside the container 87 | /app/logs # cat output.log 88 | 2020-06-21 10:41:01.282 | INFO | __main__:login:158 - User 'admin@admin.com' successfully logged in 89 | 2020-06-21 10:41:27.442 | INFO | __main__:create_project:191 - Successfully created project my-new-test 90 | 2020-06-21 10:41:40.753 | INFO | __main__:create_user_story:236 - Successfully created user-story/feature 'create_upload_expense' 91 | ``` -------------------------------------------------------------------------------- /api/tp_api/repo/sql_injection.yaml: -------------------------------------------------------------------------------- 1 | name: SQL Injection 2 | cwe: 89 3 | description: The software constructs all or part of an SQL command using externally-influenced 4 | input from an upstream component, but it does not neutralize or incorrectly neutralizes 5 | special elements that could modify the intended SQL command when it is sent to a 6 | downstream component. 7 | mitigations: 8 | - description: Consider using ORMs (Object to Relation Mappers) to protect against SQL Injection. 9 | Nearly all of them perform Parameterized Queries and Query Encoding. 10 | phase: Architecture and Design 11 | strategy: Libraries or Frameworks 12 | - description: Process SQL queries using prepared statements, parameterized queries. 13 | Do not dynamically construct and execute query strings within these features using "exec" or similar functionality, 14 | since this may re-introduce the possibility of SQL injection. 15 | phase: Implementation 16 | - description: Specifically, follow the principle of least privilege when creating 17 | user accounts to a SQL database. The database users should only have the minimum 18 | privileges necessary to use their account. 19 | phase: Architecture and Design 20 | - description: In the context of SQL Injection, error messages revealing the structure 21 | of a SQL query can help attackers tailor successful attack strings. 22 | phase: Implementation 23 | - description: Use an application firewall that can detect attacks against this weakness. 24 | It can be beneficial in cases in which the code cannot be fixed,as an emergency prevention measure while 25 | more comprehensive software assurance measures are applied, or to provide defense in depth. 26 | phase: Operation 27 | risk: 28 | - consequence: Since SQL databases generally hold sensitive data, loss of confidentiality 29 | is a frequent problem with SQL injection vulnerabilities. 30 | type: Confidentiality 31 | - consequence: If poor SQL commands are used to check user names and passwords, it 32 | may be possible to connect to a system as another user with no previous knowledge 33 | of the password. 34 | type: Access_Control 35 | - consequence: If authorization information is held in a SQL database, it may be possible 36 | to change this information through the successful exploitation of a SQL injection 37 | vulnerability. 38 | type: Access_Control 39 | - consequence: Just as it may be possible to read sensitive information, it is also 40 | possible to make changes or even delete this information with a SQL injection 41 | attack. 42 | type: Integrity 43 | categories: [app_vulns,owasp] 44 | variants: 45 | - Blind SQL Injection 46 | - Time-Based SQL Injection 47 | - Union-Based SQL Injection 48 | - Error-Based SQL Injection 49 | - Second-Order SQL Injection 50 | test-cases: 51 | - name: automated-vulnerability-scanning 52 | test: run automated vulnerability discovery tools and its Injection payloads against the application 53 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 54 | type: discovery 55 | - name: manual 56 | test: test for SQL Injection variants manually with pentesters, bug-bounty 57 | type: manual 58 | - name: exploit 59 | test: Run exploit tools to identify and exploit SQL Injections in the application 60 | type: exploit 61 | tools: [sqlmap,netsparker,jsqli] 62 | - name: source-composition-scanning 63 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of SQL Injection 64 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 65 | type: sca 66 | - name: static-analysis 67 | test: Run Static Analysis tools to identify instances of dynamic queries or stored procedures that are vulnerable to SQL Injection 68 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity] 69 | type: sast -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/scan.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | data: [], 7 | projectScanData: [], 8 | individualScanData: [] 9 | }); 10 | export const mutations = { 11 | IS_PAGE_LOADING(state, data) { 12 | state.isLoading = data; 13 | }, 14 | FETCH_DATA(state, data) { 15 | state.data = data; 16 | }, 17 | FETCH_SCAN_DATA_PROJECT(state, data) { 18 | state.projectScanData = data; 19 | }, 20 | FETCH_INDIVIDUAL_DATA(state, data) { 21 | state.individualScanData = data; 22 | } 23 | }; 24 | export const actions = { 25 | showPageLoading({ commit }, data) { 26 | commit("IS_PAGE_LOADING", data); 27 | }, 28 | fetchScanData({ commit }) { 29 | axios 30 | .get("/api/scan/read", { 31 | headers: { 32 | Authorization: localStorage.getItem("token") 33 | } 34 | }) 35 | .then(response => { 36 | if (response.data.success) { 37 | const scanData = []; 38 | for (const data of response.data.data) { 39 | scanData.push({ name: data.name }); 40 | } 41 | commit("FETCH_DATA", scanData); 42 | commit("IS_PAGE_LOADING", false); 43 | } 44 | commit("PAGE_LOADING", false); 45 | }) 46 | .catch(error => { 47 | if (error.response.status === 401) { 48 | commit("PAGE_LOADING", false); 49 | } 50 | }); 51 | }, 52 | fetchIndividualScanData({ commit }, payload) { 53 | axios 54 | .post("/api/scan-vuls/project", payload, { 55 | headers: { 56 | Authorization: localStorage.getItem("token") 57 | } 58 | }) 59 | .then(response => { 60 | if (response.data.success) { 61 | const scanData = []; 62 | for (const data of response.data.data) { 63 | scanData.push({ 64 | name: data.name, 65 | cwe: data.cwe, 66 | severity: data.severity, 67 | description: data.description 68 | }); 69 | } 70 | commit("FETCH_INDIVIDUAL_DATA", scanData); 71 | commit("IS_PAGE_LOADING", false); 72 | } 73 | commit("PAGE_LOADING", false); 74 | }) 75 | .catch(error => { 76 | if (error.response.status === 401) { 77 | commit("PAGE_LOADING", false); 78 | } 79 | }); 80 | }, 81 | fetchScanbyProject({ commit }, payload) { 82 | axios 83 | .post("/api/scan/project", payload, { 84 | headers: { 85 | Authorization: localStorage.getItem("token") 86 | } 87 | }) 88 | .then(response => { 89 | if (response.data.success) { 90 | const scanData = []; 91 | for (const scan of response.data.data.data) { 92 | scanData.push({ 93 | name: scan.name, 94 | scan_type: scan.scan_type, 95 | tool: scan.tool 96 | }); 97 | } 98 | commit("FETCH_SCAN_DATA_PROJECT", scanData); 99 | commit("IS_PAGE_LOADING", false); 100 | } 101 | commit("PAGE_LOADING", false); 102 | }) 103 | .catch(error => { 104 | if (error.response.status === 401) { 105 | commit("PAGE_LOADING", false); 106 | } 107 | }); 108 | } 109 | }; 110 | export const getters = { 111 | isPageLoading(state) { 112 | return state.isLoading; 113 | }, 114 | getScanCount(state) { 115 | if (state.data) { 116 | return state.data.length; 117 | } 118 | }, 119 | getScanProjectData(state) { 120 | if (state.projectScanData) { 121 | return state.projectScanData; 122 | } 123 | }, 124 | getScanIndividualData(state) { 125 | if (state.individualScanData) { 126 | return state.individualScanData; 127 | } 128 | } 129 | }; 130 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/projects/_name/project.vue: -------------------------------------------------------------------------------- 1 | 25 | 118 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/index.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 140 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/components/projects/ProjectsPage.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 146 | -------------------------------------------------------------------------------- /api/tp_api/repo/code_injection.yaml: -------------------------------------------------------------------------------- 1 | name: Code Injection 2 | description: The software constructs all or part of a code segment using externally-influenced 3 | input from an upstream component, but it does not neutralize or incorrectly neutralizes 4 | special elements that could modify the syntax or behavior of the intended code segment. 5 | categories: [app_vulns,owasp] 6 | cwe: 94 7 | mitigations: 8 | - description: Refactor your program so that you do not have to dynamically generate 9 | code. 10 | phase: Architecture and Design 11 | - description: To reduce the likelihood of code injection, use stringent whitelists 12 | that limit which constructs are allowed. If you are dynamically constructing code 13 | that invokes a function, then verifying that the input is alphanumeric might be 14 | insufficient. An attacker might still be able to reference a dangerous function 15 | that you did not intend to allow, such as system(), exec(), or exit(). 16 | phase: Implementation 17 | - description: Use automated static analysis tools that target this type of weakness. 18 | Many modern techniques use data flow analysis to minimize the number of false 19 | positives. This is not a perfect solution, since 100% accuracy and coverage are 20 | not feasible. 21 | phase: Testing 22 | - description: Use dynamic tools and techniques that interact with the software using 23 | large test suites with many diverse inputs, such as fuzz testing (fuzzing), robustness 24 | testing, and fault injection. The software's operation may slow down, but it should 25 | not become unstable, crash, or generate incorrect results. 26 | phase: Testing 27 | - description: Run the code in an environment that performs automatic taint propagation 28 | and prevents any command execution that uses tainted variables, such as Perl's 29 | "-T" switch. This will force the program to perform validation steps that remove 30 | the taint, although you must be careful to correctly validate your inputs so that 31 | you do not accidentally mark dangerous inputs as untainted (see CWE-183 and CWE-184). 32 | phase: Operation 33 | - description: Identify vulnerabilities in underlying libraries with Source Composition Scanning 34 | phase: Testing 35 | - description: Consider using low-logic templating systems, if you are using Templating Frameworks. Low-Logic 36 | Templating Systems like Mustache cannot expand objects and data-structures, thereby ensuring that remote code 37 | execution through Template Variables, does not happen 38 | phase: Implementation 39 | risk: 40 | - consequence: In some cases, injectable code controls authentication; this may lead 41 | to a remote vulnerability. 42 | type: Access_Control 43 | - consequence: Injected code can access resources that the attacker is directly prevented 44 | from accessing. 45 | type: Access_Control 46 | - consequence: Code injection attacks can lead to loss of data integrity in nearly 47 | all cases as the control-plane data injected is always incidental to data recall 48 | or writing. Additionally, code injection can often result in the execution of 49 | arbitrary code. 50 | type: Integrity 51 | - consequence: Often the actions performed by injected control code are unlogged. 52 | type: Non-Repudiation 53 | variants: 54 | - Server-Side Eval Injection 55 | - Server-Side Template Injection 56 | test-cases: 57 | - name: automated-vulnerability-scanning 58 | test: run automated vulnerability discovery tools and its Injection payloads against the application 59 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 60 | type: discovery 61 | - name: manual 62 | test: test for Code Injection variants manually with pentesters, bug-bounty 63 | type: manual 64 | - name: exploit 65 | test: Run exploit tools to identify and exploit Code Injections in the application 66 | type: exploit 67 | tools: [sqlmap,netsparker,jsqli] 68 | - name: source-composition-scanning 69 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of Code Injection 70 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 71 | type: sca 72 | - name: static-analysis 73 | test: Run Static Analysis tools to identify instances of dynamic queries or stored procedures that are vulnerable to Code Injection 74 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity] 75 | type: sast 76 | related_cwes: 77 | - 95 78 | - 96 -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/layouts/main.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 129 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/userStory.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | data: [], 7 | userStorydata: [], 8 | projecytuserStorydata: [], 9 | projecytuserStoryID: [], 10 | projecytuserStoryTree: [], 11 | abuserStoryCount: 0 12 | }); 13 | export const mutations = { 14 | IS_PAGE_LOADING(state, data) { 15 | state.isLoading = data; 16 | }, 17 | FETCH_DATA(state, data) { 18 | state.data = data; 19 | }, 20 | FETCH_PROJECT_USER_STORY_DATA(state, data) { 21 | state.projecytuserStorydata = data; 22 | }, 23 | FETCH_PROJECT_ABUSER_STORY_COUNT(state, data) { 24 | state.abuserStoryCount = data; 25 | }, 26 | FETCH_PROJECT_USER_STORY_ID(state, data) { 27 | state.projecytuserStoryID = data; 28 | }, 29 | FETCH_PROJECT_USER_STORY_TREE(state, data) { 30 | state.projecytuserStoryTree = data; 31 | } 32 | }; 33 | export const actions = { 34 | showPageLoading({ commit }, data) { 35 | commit("IS_PAGE_LOADING", data); 36 | }, 37 | fetchUserStoryData({ commit }) { 38 | axios 39 | .get("/api/feature/read", { 40 | headers: { 41 | Authorization: localStorage.getItem("token") 42 | } 43 | }) 44 | .then(response => { 45 | if (response.data.success) { 46 | const UserStoryData = []; 47 | for (const data of response.data.data) { 48 | UserStoryData.push({ 49 | name: data.short_name 50 | }); 51 | } 52 | commit("FETCH_DATA", UserStoryData); 53 | commit("IS_PAGE_LOADING", false); 54 | } 55 | commit("PAGE_LOADING", false); 56 | }) 57 | .catch(error => { 58 | if (error.response.status === 401) { 59 | commit("PAGE_LOADING", false); 60 | } 61 | }); 62 | }, 63 | fetchUserStoryByProject({ commit }, payload) { 64 | axios 65 | .post("/api/feature/read", payload, { 66 | headers: { 67 | Authorization: localStorage.getItem("token") 68 | } 69 | }) 70 | .then(res => { 71 | if (res.data.success) { 72 | const uStoryData = []; 73 | const aStoryData = []; 74 | const uStoryid = []; 75 | for (const u of res.data.data) { 76 | uStoryData.push({ 77 | name: u.short_name 78 | }); 79 | uStoryid.push(u._id.$oid); 80 | for (const c in u.abuses) { 81 | aStoryData.push(c.$oid); 82 | } 83 | } 84 | commit("FETCH_PROJECT_ABUSER_STORY_COUNT", aStoryData.length); 85 | commit("FETCH_PROJECT_USER_STORY_DATA", uStoryData); 86 | commit("FETCH_PROJECT_USER_STORY_ID", uStoryid); 87 | } 88 | commit("IS_FETCHING", false); 89 | }) 90 | .catch(error => { 91 | commit("IS_FETCHING", false); 92 | if (error.response.status === 401) { 93 | } 94 | }); 95 | }, 96 | fetchUserStoryTreeByProject({ commit }, payload) { 97 | axios 98 | .post("/api/user-story/project", payload, { 99 | headers: { 100 | Authorization: localStorage.getItem("token") 101 | } 102 | }) 103 | .then(res => { 104 | if (res.data.success) { 105 | commit("FETCH_PROJECT_USER_STORY_TREE", res.data.data); 106 | } 107 | commit("IS_FETCHING", false); 108 | }) 109 | .catch(error => { 110 | commit("IS_FETCHING", false); 111 | if (error.response.status === 401) { 112 | } 113 | }); 114 | } 115 | }; 116 | export const getters = { 117 | isPageLoading(state) { 118 | return state.isLoading; 119 | }, 120 | getUserStoryCount(state) { 121 | if (state.data) { 122 | return state.data.length; 123 | } 124 | }, 125 | getUserStoryProjectCount(state) { 126 | if (state.projecytuserStorydata) { 127 | return state.projecytuserStorydata.length; 128 | } 129 | }, 130 | getUserStoryProjectdata(state) { 131 | if (state.projecytuserStorydata) { 132 | return state.projecytuserStorydata; 133 | } 134 | }, 135 | getAbuserStoryProjectCount(state) { 136 | if (state.abuserStoryCount) { 137 | return state.abuserStoryCount; 138 | } 139 | }, 140 | getUserStoryProjectID(state) { 141 | if (state.projecytuserStoryID) { 142 | return state.projecytuserStoryID; 143 | } 144 | }, 145 | getUserStoryProjectTree(state) { 146 | if (state.projecytuserStoryTree) { 147 | return state.projecytuserStoryTree; 148 | } 149 | } 150 | }; 151 | -------------------------------------------------------------------------------- /api/tp_api/utils.py: -------------------------------------------------------------------------------- 1 | from mongoengine import * 2 | from loguru import logger 3 | from sys import exit 4 | import os 5 | from models import * 6 | from glob import glob 7 | import ntpath 8 | import yaml 9 | import bcrypt 10 | import csv 11 | from flask import jsonify 12 | 13 | logger.add("logs/output.log", backtrace=True, diagnose=True) 14 | 15 | 16 | def respond(success, error, message="", data=None): 17 | respond_dict = { 18 | "success": success, 19 | "error": error, 20 | } 21 | if message: 22 | respond_dict["message"] = message 23 | 24 | if data: 25 | respond_dict["data"] = data 26 | 27 | return jsonify(respond_dict) 28 | 29 | 30 | def connect_db(): 31 | if "MONGO_USER" not in os.environ: 32 | db = connect(os.environ.get("MONGO_DB", "threat_playbook")) 33 | else: 34 | try: 35 | db = connect( 36 | username=os.environ.get("MONGO_USER"), 37 | password=os.environ.get("MONGO_PASS"), 38 | host=os.environ.get("MONGO_HOST", "127.0.0.1"), 39 | db=os.environ.get("MONGO_DB", "threat_playbook"), 40 | port=int(os.environ.get("MONGO_PORT", 27017)), 41 | ) 42 | except Exception as e: 43 | logger.exception(e) 44 | print("Unable to connect to Database. Please see log for exception") 45 | exit(1) 46 | 47 | return db 48 | 49 | 50 | def load_reload_asvs_db(): 51 | if not ASVS.objects: 52 | asvs_file = os.path.join(os.path.abspath(os.path.curdir), "asvs/asvs.csv") 53 | if os.path.isfile(asvs_file): 54 | with open(asvs_file, "rt") as asvs_file: 55 | data = csv.reader(asvs_file) 56 | for row in data: 57 | cwe = row[7] 58 | if not cwe: 59 | cwe = 0 60 | else: 61 | cwe = int(row[7]) 62 | new_item = ASVS( 63 | section=row[0], 64 | name=row[1], 65 | item=row[2], 66 | description=row[3], 67 | l1=row[4] or False, 68 | l2=row[5] or False, 69 | l3=row[6] or False, 70 | cwe=cwe, 71 | nist=row[8] or "", 72 | ).save() 73 | 74 | 75 | def load_reload_repo_db(): 76 | if not Repo.objects: 77 | print("No Repo items found. Loading...") 78 | repo_path = os.path.join(os.path.abspath(os.path.curdir), "repo/") 79 | for single in glob(repo_path + "*.yaml"): 80 | single_name = ntpath.basename(single).split(".")[0] 81 | with open(single, "r") as rfile: 82 | rval = rfile.read() 83 | 84 | rcon = yaml.safe_load(rval) 85 | test_case_list = [] 86 | test_case_load = rcon.get("test-cases", []) 87 | try: 88 | if test_case_load: 89 | for single in test_case_load: 90 | new_test_case = RepoTestCase( 91 | name=single.get("name", ""), 92 | test_case=single.get("test", ""), 93 | tools=single.get("tools", []), 94 | type=single.get("type", ""), 95 | tags=single.get("tags", []), 96 | ).save() 97 | test_case_list.append(new_test_case.id) 98 | new_repo_object = Repo( 99 | short_name=single_name, 100 | name=rcon["name"], 101 | cwe=rcon["cwe"], 102 | description=rcon.get("description", ""), 103 | mitigations=rcon.get("mitigations", []), 104 | risks=rcon.get("mitigations", []), 105 | categories=rcon.get("categories", []), 106 | variants=rcon.get("variants", []), 107 | related_cwes=rcon.get("related_cwes", []), 108 | tests=test_case_list, 109 | ).save() 110 | except Exception as e: 111 | logger.exception(e) 112 | print("Unable to load Repo Objects. Please see log") 113 | 114 | 115 | def initialize_superuser(): 116 | if not User.objects or not User.objects(user_type="super"): 117 | if "SUPERUSER_EMAIL" not in os.environ: 118 | print("Mandatory variable SUPERUSER_EMAIL not present") 119 | exit(1) 120 | else: 121 | admin_pass = os.environ.get("SUPERUSER_PASS", "pl@yb00k1234") 122 | hash_pass = bcrypt.hashpw(admin_pass.encode(), bcrypt.gensalt()).decode() 123 | User( 124 | email=os.environ.get("SUPERUSER_EMAIL"), 125 | password=hash_pass, 126 | user_type="super", 127 | ).save() 128 | print("Initialized SuperUser with default password") 129 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/threatScenario.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | data: [], 7 | sevData: [], 8 | projectScenarioData: [], 9 | projectScenarioTree: [] 10 | }); 11 | export const mutations = { 12 | IS_PAGE_LOADING(state, data) { 13 | state.isLoading = data; 14 | }, 15 | FETCH_DATA(state, data) { 16 | state.data = data; 17 | }, 18 | FETCH_SEV_DATA(state, data) { 19 | state.sevData = data; 20 | }, 21 | FETCH_PROJECT_THREAT_SCENARIO_DATA(state, data) { 22 | state.projectScenarioData = data; 23 | }, 24 | FETCH_PROJECT_THREAT_SCENARIO_TREE(state, data) { 25 | state.projectScenarioTree = data; 26 | } 27 | }; 28 | export const actions = { 29 | showPageLoading({ commit }, data) { 30 | commit("IS_PAGE_LOADING", data); 31 | }, 32 | fetchThreatScenarioData({ commit }) { 33 | axios 34 | .get("/api/scenarios/read", { 35 | headers: { 36 | Authorization: localStorage.getItem("token") 37 | } 38 | }) 39 | .then(response => { 40 | if (response.data.success) { 41 | const threatScenarioData = []; 42 | for (const data of response.data.data) { 43 | threatScenarioData.push({ name: data.name }); 44 | } 45 | commit("FETCH_DATA", threatScenarioData); 46 | commit("IS_PAGE_LOADING", false); 47 | } 48 | commit("PAGE_LOADING", false); 49 | }) 50 | .catch(error => { 51 | if (error.response.status === 401) { 52 | commit("PAGE_LOADING", false); 53 | } 54 | }); 55 | }, 56 | fetchThreatScenarioSevData({ commit }) { 57 | axios 58 | .get("/api/scenario/severity", { 59 | headers: { 60 | Authorization: localStorage.getItem("token") 61 | } 62 | }) 63 | .then(response => { 64 | if (response.data.success) { 65 | const donutSeries = []; 66 | const highCount = []; 67 | const mediumCount = []; 68 | const lowCount = []; 69 | for (const data of response.data.data) { 70 | if (data.severity === 3) { 71 | highCount.push(data.severity); 72 | } else if (data.severity === 2) { 73 | mediumCount.push(data.severity); 74 | } else { 75 | lowCount.push(data.severity); 76 | } 77 | } 78 | donutSeries.push(highCount.length); 79 | donutSeries.push(mediumCount.length); 80 | donutSeries.push(lowCount.length); 81 | commit("FETCH_SEV_DATA", donutSeries); 82 | commit("IS_PAGE_LOADING", false); 83 | } 84 | commit("PAGE_LOADING", false); 85 | }) 86 | .catch(error => { 87 | if (error.response.status === 401) { 88 | commit("PAGE_LOADING", false); 89 | } 90 | }); 91 | }, 92 | fetchProjectThreatScenarioData({ commit }, payload) { 93 | axios 94 | .post("/api/scenarios/project", payload, { 95 | headers: { 96 | Authorization: localStorage.getItem("token") 97 | } 98 | }) 99 | .then(response => { 100 | if (response.data.success) { 101 | const threatScenarioData = []; 102 | for (const data of response.data.data) { 103 | threatScenarioData.push({ 104 | name: data.name 105 | }); 106 | } 107 | commit("FETCH_PROJECT_THREAT_SCENARIO_DATA", threatScenarioData); 108 | commit("IS_PAGE_LOADING", false); 109 | } 110 | commit("PAGE_LOADING", false); 111 | }) 112 | .catch(error => { 113 | if (error.response.status === 401) { 114 | commit("PAGE_LOADING", false); 115 | } 116 | }); 117 | }, 118 | fetchThreatScenarioTreeByProject({ commit }, payload) { 119 | axios 120 | .post("/api/threat-scenario/project", payload, { 121 | headers: { 122 | Authorization: localStorage.getItem("token") 123 | } 124 | }) 125 | .then(res => { 126 | if (res.data.success) { 127 | commit("FETCH_PROJECT_THREAT_SCENARIO_TREE", res.data.data); 128 | } 129 | commit("IS_FETCHING", false); 130 | }) 131 | .catch(error => { 132 | commit("IS_FETCHING", false); 133 | if (error.response.status === 401) { 134 | } 135 | }); 136 | } 137 | }; 138 | export const getters = { 139 | get_project_scenario_count(state) { 140 | if (state.projectScenarioData) { 141 | return state.projectScenarioData.length; 142 | } 143 | }, 144 | isPageLoading(state) { 145 | return state.isLoading; 146 | }, 147 | getThreatScenarioCount(state) { 148 | if (state.data) { 149 | return state.data.length; 150 | } 151 | }, 152 | getThreatScenarioSevData(state) { 153 | if (state.sevData) { 154 | return state.sevData; 155 | } 156 | }, 157 | getThreatScenarioProjectTree(state) { 158 | if (state.projectScenarioTree) { 159 | return state.projectScenarioTree; 160 | } 161 | } 162 | }; 163 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/components/project/Header.vue: -------------------------------------------------------------------------------- 1 | 106 | 168 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/store/vulnerability.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | const loginUrl = process.env.VUE_APP_API_URL; 3 | 4 | export const state = () => ({ 5 | isLoading: false, 6 | data: [], 7 | sevData: [], 8 | projectVuls: [], 9 | projectVulSevs: [], 10 | projectVulData: [], 11 | asvsData: [] 12 | }); 13 | export const mutations = { 14 | IS_PAGE_LOADING(state, data) { 15 | state.isLoading = data; 16 | }, 17 | FETCH_DATA(state, data) { 18 | state.data = data; 19 | }, 20 | FETCH_SEV_DATA(state, data) { 21 | state.sevData = data; 22 | }, 23 | FETCH_VULS_PROJECT(state, data) { 24 | state.projectVuls = data; 25 | }, 26 | FETCH_VULS_SEV_PROJECT(state, data) { 27 | state.projectVulSevs = data; 28 | }, 29 | FETCH_ASVS_DATA(state, data) { 30 | state.asvsData = []; 31 | state.asvsData = data; 32 | } 33 | }; 34 | export const actions = { 35 | showPageLoading({ commit }, data) { 36 | commit("IS_PAGE_LOADING", data); 37 | }, 38 | fetchVulnerabilitySevData({ commit }) { 39 | axios 40 | .get("/api/vulnerability/severity", { 41 | headers: { 42 | Authorization: localStorage.getItem("token") 43 | } 44 | }) 45 | .then(response => { 46 | if (response.data.success) { 47 | const pieChart = []; 48 | const highCount = []; 49 | const mediumCount = []; 50 | const lowCount = []; 51 | for (const data of response.data.data) { 52 | if (data.severity === 3) { 53 | highCount.push(data.severity); 54 | } else if (data.severity === 2) { 55 | mediumCount.push(data.severity); 56 | } else { 57 | lowCount.push(data.severity); 58 | } 59 | } 60 | pieChart.push(highCount.length); 61 | pieChart.push(mediumCount.length); 62 | pieChart.push(lowCount.length); 63 | commit("FETCH_SEV_DATA", pieChart); 64 | commit("IS_PAGE_LOADING", false); 65 | } 66 | commit("PAGE_LOADING", false); 67 | }) 68 | .catch(error => { 69 | if (error.response.status === 401) { 70 | commit("PAGE_LOADING", false); 71 | } 72 | }); 73 | }, 74 | 75 | fetchVulnerabilitybyProject({ commit }, payload) { 76 | axios 77 | .post("/api/vulnerability/project", payload, { 78 | headers: { 79 | Authorization: localStorage.getItem("token") 80 | } 81 | }) 82 | .then(response => { 83 | if (response.data.success) { 84 | const vulData = []; 85 | const pieSeries = []; 86 | const highPieCount = []; 87 | const mediumPieCount = []; 88 | const lowPieCount = []; 89 | for (const vul of response.data.data.data) { 90 | vulData.push({ 91 | name: vul.name, 92 | cwe: vul.cwe, 93 | severity: vul.severity, 94 | description: vul.description 95 | }); 96 | if (vul.severity === 3) { 97 | highPieCount.push(vul.severity); 98 | } else if (vul.severity === 2) { 99 | mediumPieCount.push(vul.severity); 100 | } else { 101 | lowPieCount.push(vul.severity); 102 | } 103 | } 104 | pieSeries.push(highPieCount.length); 105 | pieSeries.push(mediumPieCount.length); 106 | pieSeries.push(lowPieCount.length); 107 | commit("FETCH_VULS_PROJECT", vulData); 108 | commit("FETCH_VULS_SEV_PROJECT", pieSeries); 109 | commit("IS_PAGE_LOADING", false); 110 | } 111 | commit("PAGE_LOADING", false); 112 | }) 113 | .catch(error => { 114 | if (error.response.status === 401) { 115 | commit("PAGE_LOADING", false); 116 | } 117 | }); 118 | }, 119 | 120 | fetchASVSbyProject({ commit }, payload) { 121 | axios 122 | .post("/api/asvs", payload, { 123 | headers: { 124 | Authorization: localStorage.getItem("token") 125 | } 126 | }) 127 | .then(response => { 128 | if (response.data.success) { 129 | const vulData = []; 130 | for (const vul of response.data.data) { 131 | vulData.push({ 132 | name: vul.name, 133 | cwe: vul.cwe, 134 | item: vul.item, 135 | description: vul.description, 136 | l1: vul.l1, 137 | l2: vul.l2, 138 | l3: vul.l3, 139 | section: vul.section 140 | }); 141 | } 142 | commit("FETCH_ASVS_DATA", vulData); 143 | commit("IS_PAGE_LOADING", false); 144 | } 145 | commit("PAGE_LOADING", false); 146 | }) 147 | .catch(error => { 148 | if (error.response.status === 401) { 149 | commit("PAGE_LOADING", false); 150 | } 151 | }); 152 | } 153 | }; 154 | export const getters = { 155 | isPageLoading(state) { 156 | return state.isLoading; 157 | }, 158 | getVulnerabilitySevData(state) { 159 | if (state.sevData) { 160 | return state.sevData; 161 | } 162 | }, 163 | getVulnerabilityProjectCount(state) { 164 | if (state.projectVuls) { 165 | return state.projectVuls.length; 166 | } 167 | }, 168 | getVulnerabilityProjectSev(state) { 169 | if (state.projectVulSevs) { 170 | return state.projectVulSevs; 171 | } 172 | }, 173 | getVulnerabilityProjectData(state) { 174 | if (state.projectVuls) { 175 | return state.projectVuls; 176 | } 177 | }, 178 | getASVSData(state) { 179 | if (state.asvsData) { 180 | return state.asvsData; 181 | } 182 | } 183 | }; 184 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/home/index.vue: -------------------------------------------------------------------------------- 1 | 103 | 180 | -------------------------------------------------------------------------------- /documentation/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/tp_api/repo/xss.yaml: -------------------------------------------------------------------------------- 1 | name: Cross-Site Scripting 2 | cwe: 79 3 | description: The software does not neutralize or incorrectly neutralizes user-controllable 4 | input before it is placed in output that is used as a web page that is served to 5 | other users. 6 | mitigations: 7 | - description: Examples of libraries and frameworks that make it easier to generate 8 | properly encoded output include Microsoft's Anti-XSS library, the OWASP ESAPI 9 | Encoding module, and Apache Wicket. 10 | phase: Architecture and Design 11 | - description: Understand all the potential areas where untrusted inputs can enter 12 | your software. Parameters or arguments, cookies, anything read from the network, 13 | environment variables, reverse DNS lookups, query results, request headers, URL 14 | components, e-mail, files, filenames, databases, and any external systems that 15 | provide data to the application. Remember that such inputs may be obtained indirectly 16 | through API calls. 17 | phase: Architecture and Design 18 | - description: For any security checks that are performed on the client side, ensure 19 | that these checks are duplicated on the server side. 20 | phase: Architecture and Design 21 | - description: If available, use structured mechanisms that automatically enforce 22 | the separation between data and code. These mechanisms may be able to provide 23 | the relevant quoting, encoding, and validation automatically, instead of relying 24 | on the developer to provide this capability at every point where output is generated. 25 | phase: Architecture and Design 26 | - description: The problem of inconsistent output encodings often arises in web pages. 27 | If an encoding is not specified in an HTTP header, web browsers often guess about 28 | which encoding is being used. This can open up the browser to subtle XSS attacks. 29 | phase: Implementation 30 | - description: To help mitigate XSS attacks against the user's session cookie, set 31 | the session cookie to be HttpOnly. In browsers that support the HttpOnly feature 32 | (such as more recent versions of Internet Explorer and Firefox), this attribute 33 | can prevent the user's session cookie from being accessible to malicious client-side 34 | scripts that use document.cookie. This is not a complete solution, since HttpOnly 35 | is not supported by all browsers. More importantly, XMLHTTPRequest and other powerful 36 | browser technologies provide read access to HTTP headers, including the Set-Cookie 37 | header in which the HttpOnly flag is set. 38 | phase: Implementation 39 | - description: Ensure that you perform input validation at well-defined interfaces 40 | within the application. This will help protect the application even if a component 41 | is reused or moved elsewhere. 42 | phase: Implementation 43 | - description: When the set of acceptable objects, such as filenames or URLs, is limited 44 | or known, create a mapping from a set of fixed input values (such as numeric IDs) 45 | to the actual filenames or URLs, and reject all other inputs. 46 | phase: Architecture and Design 47 | - description: Use an application firewall that can detect attacks against this weakness. 48 | It can be beneficial in cases in which the code cannot be fixed (because it is 49 | controlled by a third party), as an emergency prevention measure while more comprehensive 50 | software assurance measures are applied, or to provide defense in depth. 51 | phase: Operation 52 | - description: Use a Content-Security-Policy Header for your application that is honored by a modern browser. 53 | Content Security Policy allows you to define specific execution scopes for attributes and elements that can be used 54 | to perform Cross-Site Scripting. 55 | phase: Implementation 56 | - description: Deploy a Cross-Origin-Resource Sharing implementation to prevent your application's front-end from accessing 57 | unauthorized third-party URLs that may be part of an attacker's Cross-Site Scripting Attack 58 | phase: Implementation 59 | categories: [app_vulns,owasp] 60 | risk: 61 | - consequence: The most common attack performed with cross-site scripting involves 62 | the disclosure of information stored in user cookies. Typically, a malicious user 63 | will craft a client-side script, which -- when parsed by a web browser -- performs 64 | some activity (such as sending all site cookies to a given E-mail address). This 65 | script will be loaded and run by each user visiting the web site. Since the site 66 | requesting to run the script has access to the cookies in question, the malicious 67 | script does also. 68 | type: Access_Control 69 | - consequence: In some circumstances it may be possible to run arbitrary code on a 70 | victim's computer when cross-site scripting is combined with other flaws. 71 | type: Integrity 72 | - consequence: The consequence of an XSS attack is the same regardless of whether 73 | it is stored or reflected. The difference is in how the payload arrives at the 74 | server. 75 | type: Confidentiality 76 | variants: 77 | - Persistent Cross-Site Scripting 78 | - Reflected Cross-Site Scripting 79 | - DOM-Based Cross Site Scripting 80 | test-cases: 81 | - name: automated-vulnerability-scanning 82 | test: run automated vulnerability discovery tools and its XSS payloads against the application 83 | tools: [zap,burpsuite,arachni,acunetix,netsparker,appspider,w3af] 84 | type: discovery 85 | - name: manual 86 | test: test for XSS variants manually with pentesters, bug-bounty 87 | type: manual 88 | - name: content-security-policy 89 | test: Look for Content Security Policy headers in the Application and validate if the application enforces content-security-policy headers with optimal efficacy 90 | type: recon 91 | - name: cors 92 | test: Look for Cross-Origin Resource Sharing to ensure that the application is unable to make calls to URLs outside a specific set of whitelisted URLs and domains 93 | type: discovery 94 | - name: static-analysis 95 | test: Run Static Analysis tools to identify instances of improper input validation and output encoding for Cross-Site Scripting 96 | tools: [checkmarx,brakeman,bandit,pyt,security-code-scan,veracode,nodejsscan,coverity,] 97 | type: sast 98 | - name: source-composition-scanning 99 | test: Run Source Composition Scanners against the libraries being used by the application, to identify instances of Cross-Site Scripting 100 | tools: [retirejs,npm-audit,owasp-dependency-checker,blackduck,whitesource,snyk,safety] 101 | type: sca 102 | related_cwes: 103 | - 80 104 | - 116 105 | 106 | -------------------------------------------------------------------------------- /api/tp_api/models.py: -------------------------------------------------------------------------------- 1 | from mongoengine import * 2 | import datetime 3 | from uuid import uuid4 4 | from hashlib import sha256 5 | from mongoengine import signals 6 | 7 | 8 | def random_scan_name(): 9 | return str(uuid4()) 10 | 11 | 12 | class Project(Document): 13 | name = StringField(max_length=100, required=True, unique=True) 14 | orchy_webhook = StringField(required=False) 15 | features = ListField(ReferenceField("UseCase"), required=False) 16 | 17 | 18 | class RepoTestCase(Document): 19 | name = StringField() 20 | test_case = StringField() 21 | executed = BooleanField(default=False) 22 | tools = ListField(StringField()) 23 | type = StringField(max_length=20) 24 | tags = ListField(StringField()) 25 | 26 | 27 | class Repo(Document): 28 | short_name = StringField(required=True) 29 | name = StringField(required=True) 30 | cwe = IntField(required=True) 31 | description = StringField() 32 | variants = ListField(StringField()) 33 | categories = ListField(StringField()) 34 | mitigations = ListField(DictField()) 35 | risks = ListField(DictField()) 36 | tests = ListField(ReferenceField(RepoTestCase)) 37 | related_cwes = ListField(IntField()) 38 | 39 | 40 | class Interaction(Document): 41 | nature_choices = (("I", "Internal"), ("E", "External")) 42 | nature = StringField(choices=nature_choices) 43 | endpoint = StringField() 44 | data_flow = StringField() 45 | project = ReferenceField(Project, reverse_delete_rule=CASCADE) 46 | 47 | 48 | class UseCase(Document): 49 | short_name = StringField(unique=True) 50 | description = StringField() 51 | project = ReferenceField(Project, reverse_delete_rule=CASCADE, required=True) 52 | relations = ListField(ReferenceField(Interaction)) 53 | boundary = StringField() 54 | abuses = ListField(ReferenceField("AbuseCase")) 55 | 56 | 57 | class AbuseCase(Document): 58 | short_name = StringField(max_length=100, unique=True) 59 | description = StringField() 60 | use_case = ReferenceField(UseCase, reverse_delete_rule=CASCADE, required=True) 61 | scenarios = ListField(ReferenceField("ThreatModel"), required=False) 62 | 63 | 64 | model_type_choices = (("repo", "repo"), ("inline", "inline")) 65 | 66 | 67 | class Mitigations(EmbeddedDocument): 68 | phase = StringField() 69 | strategy = StringField() 70 | description = StringField() 71 | code = StringField() 72 | 73 | 74 | class Risk(EmbeddedDocument): 75 | description = StringField() 76 | phase = StringField() 77 | 78 | 79 | class ThreatModel(Document): 80 | # meta = {'collection': 'threat_model'} 81 | name = StringField() 82 | vul_name = StringField() 83 | description = StringField(required=True) 84 | severity = IntField() 85 | model_type = StringField(max_length=6, choices=model_type_choices) 86 | repo_vul_name = ReferenceField(Repo) 87 | use_case = ReferenceField(UseCase, reverse_delete_rule=CASCADE) 88 | abuse_case = ReferenceField(AbuseCase, reverse_delete_rule=CASCADE) 89 | cwe = IntField() 90 | risks = EmbeddedDocumentListField(Risk) 91 | categories = ListField(StringField(max_length=30)) 92 | mitigations = EmbeddedDocumentListField(Mitigations) 93 | tests = ListField(ReferenceField("Test")) 94 | 95 | 96 | class Test(Document): 97 | name = StringField() 98 | test_case = StringField() 99 | executed = BooleanField(default=False) 100 | tools = ListField(StringField()) 101 | test_type = StringField() 102 | tags = ListField(StringField()) 103 | scenario = ReferenceField(ThreatModel, reverse_delete_rule=CASCADE) 104 | 105 | 106 | class Risk(EmbeddedDocument): 107 | consequence = StringField() 108 | risk_type = StringField() 109 | 110 | 111 | class VulnerabilityEvidence(Document): 112 | evidence_type = StringField() 113 | log = StringField() 114 | url = StringField() 115 | line_num = IntField() 116 | param = StringField() 117 | attack = StringField() 118 | info = StringField() 119 | vuln = ReferenceField("Vulnerability") 120 | 121 | 122 | class Vulnerability(Document): 123 | severity_choices = ((3, "High"), (2, "Medium"), (1, "Low"), (0, "Info")) 124 | tool = StringField() 125 | name = StringField() 126 | cwe = IntField() 127 | severity = IntField(choices=severity_choices) 128 | description = StringField() 129 | observation = StringField() 130 | remediation = StringField() 131 | evidences = ListField(ReferenceField(VulnerabilityEvidence)) 132 | created_on = DateTimeField(default=datetime.datetime.utcnow) 133 | scan = ReferenceField("Scan") 134 | target = ReferenceField("Target") 135 | 136 | 137 | class Scan(Document): 138 | created_on = DateTimeField(default=datetime.datetime.utcnow) 139 | name = StringField(unique=True) 140 | vulnerabilities = ListField(ReferenceField("Vulnerability")) 141 | target = ReferenceField("Target") 142 | tool = StringField() 143 | scan_type = StringField( 144 | choices=( 145 | ("SAST", "Static Analysis"), 146 | ("DAST", "Dynamic Analysis"), 147 | ("SCA", "Source Composition Analysis"), 148 | ("IAST", "Interactive Analysis"), 149 | ("Manual", "Manual Scan"), 150 | ) 151 | ) 152 | 153 | @classmethod 154 | def pre_save(cls, sender, document, **kwargs): 155 | document.name = "{}-scan-{}-{}@{}".format( 156 | document.tool, 157 | document.target.name, 158 | document.target.project.name, 159 | datetime.datetime.utcnow().isoformat(), 160 | ) 161 | 162 | 163 | class Target(Document): 164 | name = StringField(unique=True) 165 | url = StringField() 166 | project = ReferenceField(Project, reverse_delete_rule=CASCADE) 167 | scans = ListField(ReferenceField(Scan)) 168 | 169 | 170 | class User(Document): 171 | user_type_choices = (("super", "superuser"), ("user", "user")) 172 | email = StringField(max_length=100, unique=True) 173 | password = StringField(max_length=100) 174 | user_type = StringField(choices=user_type_choices, max_length=6, default="user") 175 | default_password = BooleanField(default=True) 176 | 177 | 178 | class Settings(Document): 179 | orchy_url = StringField() 180 | orchy_user = StringField() 181 | orchy_password = StringField() 182 | 183 | 184 | class ASVS(Document): 185 | section = StringField() 186 | name = StringField() 187 | item = StringField() 188 | description = StringField() 189 | l1 = BooleanField(default=False) 190 | l2 = BooleanField(default=False) 191 | l3 = BooleanField(default=False) 192 | cwe = IntField() 193 | nist = StringField() 194 | 195 | 196 | signals.pre_save.connect(Scan.pre_save, sender = Scan) -------------------------------------------------------------------------------- /documentation/docs/story-driven.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: story-driven 3 | title: Story-Driven Threat Modeling with ThreatPlaybook 4 | sidebar_label: Story-Driven Threat Modeling 5 | --- 6 | 7 | ## Objective 8 | In this tutorial, you will learn how you can create a story-driven threat model with ThreatPlaybook 9 | 10 | ## What is a Story-Driven Threat Model? 11 | ### User-Story Driven Threat Modeling 12 | Story-Driven Threat Modeling is different from System-Driven Threat Modeling that we are largely used to. Story-driven threat modeling starts with modeling user-stories (or feature definitions). The primary reasons behind story-driven threat modeling are: 13 | * **Speed** - Your Threat Models are able to keep pace with the Engineering Team's SDLC rather than a long-drawn system-wide threat model being performed on a periodic basis 14 | * **Focus** - The focus of your threat model in case of a story-driven threat model is the user-story itself. You waste little/no time on analyzing too many tangents 15 | * **Iterative** - Story-driven threat modeling evolves with the story. If the story changes, the threat model might change. It lends itself to more scalability and elasticity 16 | 17 | #### User Story/Feature 18 | The User Story is a Description of the Functionality. This concept is very popular in the world of Agile Development, where an Agile Team picks a bunch of user stories to get done in a sprint. It is a Unit of Work to be completed in a sprint. A user story typically looks like this 19 | > As a user (salesperson), I should be able to access my customer's profile to be able to log call information with the customer 20 | 21 | #### Abuser Story 22 | An Abuser Story is an "evil user's version" of a user story. An Abuser Story captures (at a high level) WHAT a threat actor can do to abuse the feature enumerated in the User Story. For example 23 | > As a rival salesperson, I will access other salespeople's customers in the application to poach customers from my colleagues 24 | 25 | #### Threat Model/Scenario 26 | A Threat Model/Scenario is a description of HOW a threat actor can bring the abuser story to life. This is a scenario detailing the Attack Vector (primary technique for attack) and approach to bringing the Abuser Story to life. One can use STRIDE as an approach to capture various threats and use DREAD/CVSSv3 to capture impact of said attack. For example: 27 | 28 | > Abuser Story: As a rival salesperson, I will access other salespeople's customers in the application to poach customers from my colleagues 29 | > 30 | > Threat Models: 31 | > * Attacker performs SQL Injection, to gain access to the Database of other customers in the System 32 | > * Attacker attempts to perform Insecure Direct Object Reference attacks, by incrementing the customer's ID value, gaining access to another customer's account 33 | > * Attacker steals a rival's session tokens by performing a Man-in-the-Middle Attack 34 | > .... 35 | 36 | ## Story-Driven Threat Modeling with ThreatPlaybook 37 | 38 | ### Start by making sure that you have your Server and CLI setup 39 | 40 | > Follow Steps 1 and 2 in the Quick Start Guide here 41 | 42 | ### Let's configure the CLI first 43 | 44 | > Currently, the CLI or the REST API are the only ways that data can be loaded into ThreatPlaybook. WebUI is view-only 45 | 46 | ##### Create a Project Directory 47 | ``` 48 | mkdir -p /your/project/directory 49 | ``` 50 | 51 | ##### `cd` into your project directory 52 | ``` 53 | cd /your/project/directory 54 | ``` 55 | 56 | ##### Configure 57 | 58 | ``` 59 | playbook configure -e -u -p 60 | ``` 61 | 62 | > Please ensure that you substitute the following: 63 | > * `your-email` enter any email address, eample: admin@admin.com 64 | > * `host-info` for your addressable IP Address, example: 192.168.1.17 65 | > * `port` for the nginx port, example: 80 66 | 67 | After this, the CLI prompts you for a password, which is the super-admin password that you've setup in Step 1 68 | 69 | ### Let's create a Project 70 | 71 | > A Project is typically an application that you want to Threat Model. In the case of a micro-service, you may want to use either a single project or multiple projects if they are modeled and managed separately 72 | 73 | ```bash 74 | playbook apply project -n test-project 75 | ``` 76 | > `test-project` is the name of the project, in this case 77 | 78 | ### Let's create our User-Story/Feature YAML file 79 | 80 | In ThreatPlaybook, each user-story/feature is captured "as-code" in a single YAML file. 81 | 82 | The YAML file typically has a structure of: 83 | * Feature/User-Story 84 | * Abuser-Stories under the Feature/User-Story 85 | * Threat Scenarios under the Abuser Story 86 | * Test Cases for the Threat Scenario 87 | 88 | Let's look at a sample user-story/feature yaml 89 | 90 | ```yaml 91 | objectType: Feature #this is the user story 92 | name: create_upload_expense 93 | description: As a user, I am able to create and upload expenses within project limit that have been incurred by me for processing/payment by my manager, so I can get reimbursed 94 | abuse_cases: 95 | - name: manipulate expense information #this is an abuser story 96 | description: As a malicious user, I will manipulate expense management process to get larger or bogus expenses into the system. 97 | threat_scenarios: # these are the list of threat scenarios for the abuser story 98 | - name: sql injection expense limit bypass 99 | type: repo # repo type threat scenario 100 | description: Perform SQL Injection to compromise the Database, and raise project budget limits or bypass expense controls 101 | reference: {name: sql_injection, severity: 3} 102 | - name: upload-malware 103 | type: inline # inline type threat scenario 104 | description: I will upload malware as an expense to the system and compromise the application and create a DoS condition 105 | type: inline 106 | vul_name: Malicious File Upload 107 | severity: 3 108 | cwe: 434 109 | test-cases: 110 | - name: manual-pentesting 111 | test: upload files with reverse-shell and CSV injection payloads and attempt to trigger remote code execution on Project Manager's Computer 112 | type: exploit 113 | tools: manual 114 | ``` 115 | 116 | #### Let's examine this YAML file in more detail and some tips 117 | 118 | * ThreatPlaybook is name-based, so its important that you name your features/abuser-stories/threat-scenarios uniquely 119 | * Threat Scenarios can either be `repo` type or `inline` type: 120 | * **Repo Type**: ThreatPlaybook has a list of canned Vulnerability Definitions already pre-loaded in the system. Of course, you can create your own definitions as well. These form a repository or `repo`. So, when you want to declare a threat scenario is a `repo` type threat scenario, all you have to do is refer to the name of your canned vulnerability, and ThreatPlaybook automatically enumerates the following from the repo: 121 | * CWE 122 | * Mitigations 123 | * Test cases 124 | * other metadata fields 125 | * **Inline type**: When you declare and use inline threat scenarios, you are defining and declaring the threat scenario `inline` with the YAML. In this case, there's no canned vulnerability and you are declaring that vulnerability definition on the fly. Here, you need to declare all these attributes yourself: 126 | * name 127 | * cwe 128 | * vul_name 129 | * test-cases 130 | * severity 131 | * description 132 | * test cases 133 | 134 | ### Push YAML to ThreatPlaybook server 135 | 136 | When you're done defining your YAML, all you have to do is to push the Story-driven threat model to the ThreatPlaybook Server. This can be done with: 137 | 138 | ```bash 139 | playbook apply feature -f -p test-project 140 | ``` 141 | 142 | > You'll need to provide the absolute (full) path to the yaml file. Relative paths wont work 143 | 144 | If everything has worked, you should see a string of success messages of having created objects in ThreatPlaybook -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/projects/_name/threat-map.vue: -------------------------------------------------------------------------------- 1 | 201 | 263 | 270 | -------------------------------------------------------------------------------- /frontend/Playbook-Frontend/pages/projects/_name/user-story.vue: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | 193 | 330 | -------------------------------------------------------------------------------- /documentation/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | docu_tree --------------------------------------------------------------------------------