├── .eslintrc.json ├── .github ├── FUNDING.yml ├── proposal.md └── workflows │ ├── ESLint.yml │ ├── depoy.yml │ ├── main.yml │ ├── stale.yaml │ └── tests.yml ├── .gitignore ├── .phptidy-config.php ├── .prettierignore ├── .prettierrc.json ├── .releaserc.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Caddyfile ├── Development.md ├── Dockerfile ├── LICENSE ├── README.md ├── angular.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── component │ │ ├── about-us │ │ │ ├── about-us.component.css │ │ │ ├── about-us.component.html │ │ │ ├── about-us.component.spec.ts │ │ │ └── about-us.component.ts │ │ ├── activity-description │ │ │ ├── activity-description.component.css │ │ │ ├── activity-description.component.html │ │ │ ├── activity-description.component.spec.ts │ │ │ └── activity-description.component.ts │ │ ├── circular-heatmap │ │ │ ├── circular-heatmap.component.css │ │ │ ├── circular-heatmap.component.html │ │ │ ├── circular-heatmap.component.spec.ts │ │ │ └── circular-heatmap.component.ts │ │ ├── dependency-graph │ │ │ ├── dependency-graph.component.css │ │ │ ├── dependency-graph.component.html │ │ │ ├── dependency-graph.component.spec.ts │ │ │ └── dependency-graph.component.ts │ │ ├── logo │ │ │ ├── logo.component.css │ │ │ ├── logo.component.html │ │ │ ├── logo.component.spec.ts │ │ │ └── logo.component.ts │ │ ├── mapping │ │ │ ├── mapping.component.css │ │ │ ├── mapping.component.html │ │ │ ├── mapping.component.spec.ts │ │ │ └── mapping.component.ts │ │ ├── matrix │ │ │ ├── matrix.component.css │ │ │ ├── matrix.component.html │ │ │ ├── matrix.component.spec.ts │ │ │ └── matrix.component.ts │ │ ├── modal-message │ │ │ ├── modal-message.component.css │ │ │ ├── modal-message.component.html │ │ │ ├── modal-message.component.spec.ts │ │ │ └── modal-message.component.ts │ │ ├── readme-to-html │ │ │ ├── readme-to-html.component.css │ │ │ ├── readme-to-html.component.html │ │ │ ├── readme-to-html.component.spec.ts │ │ │ └── readme-to-html.component.ts │ │ ├── sidenav-buttons │ │ │ ├── sidenav-buttons.component.css │ │ │ ├── sidenav-buttons.component.html │ │ │ ├── sidenav-buttons.component.spec.ts │ │ │ └── sidenav-buttons.component.ts │ │ ├── teams │ │ │ ├── teams.component.css │ │ │ ├── teams.component.html │ │ │ ├── teams.component.spec.ts │ │ │ └── teams.component.ts │ │ ├── top-header │ │ │ ├── top-header.component.css │ │ │ ├── top-header.component.html │ │ │ ├── top-header.component.spec.ts │ │ │ └── top-header.component.ts │ │ ├── usage │ │ │ ├── usage.component.css │ │ │ ├── usage.component.html │ │ │ ├── usage.component.spec.ts │ │ │ └── usage.component.ts │ │ └── userday │ │ │ ├── userday.component.css │ │ │ ├── userday.component.html │ │ │ ├── userday.component.spec.ts │ │ │ └── userday.component.ts │ ├── material │ │ └── material.module.ts │ ├── pipe │ │ ├── to-string-value.pipe.spec.ts │ │ └── to-string-value.pipe.ts │ └── service │ │ ├── theme.service.ts │ │ └── yaml-parser │ │ ├── yaml-parser.service.spec.ts │ │ └── yaml-parser.service.ts ├── assets │ ├── Markdown Files │ │ ├── README.md │ │ ├── USAGE.md │ │ ├── dimensions.md │ │ ├── maturity-level-0.md │ │ └── userday.md │ ├── YAML │ │ ├── generated │ │ │ └── README.md │ │ ├── meta.yaml │ │ ├── schemas │ │ │ ├── dsomm-implementations-schema.json │ │ │ ├── dsomm-schema-build-and-deployment.json │ │ │ ├── dsomm-schema-culture-and-organization.json │ │ │ ├── dsomm-schema-implementation.json │ │ │ ├── dsomm-schema-information-gathering.json │ │ │ └── dsomm-schema-test-and-verification.json │ │ └── teams.yaml │ ├── images │ │ ├── Build and Deployment.png │ │ ├── Culture and Organization.png │ │ ├── Implementation.png │ │ ├── Information Gathering.png │ │ ├── Test and Verification.png │ │ ├── logo-image.png │ │ ├── logo.png │ │ ├── sponsors │ │ │ └── heroku.png │ │ └── userday │ │ │ ├── Brook.png │ │ │ ├── Francesco.png │ │ │ ├── Jannik.jpg │ │ │ └── Timo.png │ └── presentations │ │ └── userday-sf-2024-reach-your-dynamic-depth-with-owasp-securecodebox.pdf ├── custom-theme.scss ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "plugin:@angular-eslint/template/process-inline-templates", 20 | "plugin:prettier/recommended" 21 | ], 22 | "rules": { 23 | "@angular-eslint/directive-selector": [ 24 | "error", 25 | { 26 | "type": "attribute", 27 | "prefix": "app", 28 | "style": "camelCase" 29 | } 30 | ], 31 | "@angular-eslint/component-selector": [ 32 | "error", 33 | { 34 | "type": "element", 35 | "prefix": "app", 36 | "style": "kebab-case" 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "files": [ 43 | "*.html" 44 | ], 45 | "extends": [ 46 | "plugin:@angular-eslint/template/recommended" 47 | ], 48 | "rules": {} 49 | }, 50 | { 51 | "files": ["*.html"], 52 | "excludedFiles": ["*inline-template-*.component.html"], 53 | "extends": ["plugin:prettier/recommended"], 54 | "rules": { 55 | // NOTE: WE ARE OVERRIDING THE DEFAULT CONFIG TO ALWAYS SET THE PARSER TO ANGULAR (SEE BELOW) 56 | "prettier/prettier": ["error", { "parser": "angular" }] 57 | } 58 | } 59 | 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://owasp.org/donate/?reponame=www-project-devsecops-maturity-model&title=OWASP+Devsecops+Maturity+Model 2 | github: OWASP 3 | -------------------------------------------------------------------------------- /.github/proposal.md: -------------------------------------------------------------------------------- 1 | # OWASP DSOMM Enhancement Proposal 2 | 3 | ## Overview 4 | 5 | This proposal outlines key enhancements to the OWASP DevSecOps Maturity Model (DSOMM) to improve its functionality, usability, and integration with other security frameworks. The total estimated effort for all proposed features is 268 hours (33.5 days). 6 | 7 | ## Proposed Enhancements 8 | 9 | ### 1. Vulnerability Management and Patch Management Expansion 10 | 11 | **Problem:** Current vulnerability management coverage is incomplete, particularly lacking metrics. 12 | **Solution:** Integrate concepts from the Vulnerability Management Maturity Model and "Effective Vulnerability Management" book to add: 13 | - New activities mapped to SAMM, ISO, and OpenCRE 14 | - Risk and measure descriptions 15 | - Implementation guidance 16 | - Level justifications based on effort and security value 17 | 18 | **Estimated Effort:** 32 hours 19 | 20 | ### 2. Compliance Date Integration 21 | 22 | **Problem:** Activity implementation status doesn't account for time-based assessment/compliance requirements. 23 | 24 | **Solution:** 25 | As a security architect, I want teams to perform threat modeling quaterly. 26 | As a project team, I perform a threat modeling and the status is DSOMM for that team is changed to "implemented". As there is no automatic removal of the status, it stays "implemented". 27 | 28 | Tasks: 29 | - Add `threshold` attribute to activities for time-based assessment/compliance 30 | - Enhance `teamsImplemented` attribute to track implementation dates 31 | - Update UI to display assessment/compliance status based on dates and thresholds 32 | 33 | Sample `threshold`: 34 | ``` 35 | threshold: 36 | targets: 37 | - type: "count" 38 | minValue: 1 39 | period: 40 | periodType: sliding 41 | timeframe: "2Y" 42 | ``` 43 | The `teamsImplemented` attribute, to be filled out by teams: 44 | ``` 45 | teamsImplemented: 46 | - teamA: 47 | conductionDate: 2024-08-08 00:00:00 48 | - teamB: 49 | conductionDate: 2024-08-08 00:00:00 50 | - teamB: 51 | implemented: true 52 | ``` 53 | 54 | **Estimated Effort:** 80 hours 55 | 56 | ### 3. Score Calculation 57 | 58 | **Problem:** Current visualization can be difficult to interpret quickly. 59 | 60 | **Solution:** Implement an overall score calculation for each sub-dimension, showing implemented vs. maximum possible activities for teams. 61 | 62 | **Estimated Effort:** 8 hours 63 | 64 | ### 4. Customization Capabilities 65 | 66 | **Problem:** Organizations need to adapt DSOMM for their specific security programs, which is currently challenging. 67 | 68 | **Solution:** Make the DSOMM application customizable: 69 | - Auto-adjust levels in visualizations when changed 70 | - Allow hiding/adding attributes for activity descriptions 71 | - Ensure consistent updates across linked elements (e.g., overview tables, detailed descriptions) 72 | 73 | **Estimated Effort:** 80 hours 74 | 75 | ### 5. OpenCRE Integration Enhancement 76 | 77 | **Problem:** Current OpenCRE chatbot lacks comprehensive DSOMM content integration. 78 | 79 | **Solution:** 80 | - Customize OpenCRE content with DSOMM-specific information 81 | - Provide sample pre-questions for improved DSOMM coverage 82 | - Create a guide for enhancing OpenCRE content for other projects 83 | 84 | The solution needs to be implemented together with openCRE team. 85 | 86 | **Estimated Effort:** 60 hours 87 | 88 | ### 6. Status `notApplicable` 89 | **Problem:** An application security program defines activities to be implemented by product teams. Sometimes, the activities are not applicable to a product/application. 90 | 91 | **Solution:** Add the status `notApplicable` for teams 92 | 93 | **Estimated Effort:** 8 hours 94 | 95 | ## Total Estimated Effort 96 | 97 | 268 hours (33.5 days) 98 | 99 | ## Benefits 100 | 101 | - Improved vulnerability management guidance 102 | - Better compliance tracking and reporting 103 | - Enhanced data visualization and interpretation 104 | - Increased flexibility for organizational adoption 105 | - Tighter integration with broader security ecosystems 106 | 107 | ## Conclusion 108 | 109 | These enhancements will significantly improve the usability, adaptability, and value of OWASP DSOMM for organizations implementing DevSecOps practices. The proposed changes will make DSOMM a more comprehensive and user-friendly tool for assessing and improving security maturity. 110 | -------------------------------------------------------------------------------- /.github/workflows/ESLint.yml: -------------------------------------------------------------------------------- 1 | name: ESLint Check 2 | on: [push,pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Install modules 9 | run: yarn 10 | - name: Run ESLint 11 | run: yarn run eslint . --ext .js,.jsx,.ts,.tsx 12 | -------------------------------------------------------------------------------- /.github/workflows/depoy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Heroku 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | 7 | 8 | jobs: 9 | heroku: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "Check out Git repository" 13 | uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 14 | - name: "Set Heroku app & branch for ${{ github.ref }}" 15 | run: | 16 | echo $GITHUB_REF 17 | if [ "$GITHUB_REF" == "refs/heads/master" ]; then 18 | echo "HEROKU_APP=" >> $GITHUB_ENV 19 | echo "HEROKU_BRANCH=master" >> $GITHUB_ENV 20 | fi 21 | echo "HEROKU_BRANCH=master" >> $GITHUB_ENV 22 | - name: Install Heroku CLI 23 | run: | 24 | curl https://cli-assets.heroku.com/install.sh | sh 25 | - name: "Deploy ${{ github.ref }} to Heroku" 26 | uses: akhileshns/heroku-deploy@v3.13.15 27 | with: 28 | heroku_api_key: ${{ secrets.HEROKU_API_KEY }} 29 | heroku_app_name: "dsomm" 30 | heroku_email: timo.pagel@owasp.org 31 | branch: ${{ env.HEROKU_BRANCH }} 32 | usedocker: true 33 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 7 * * *" 9 | 10 | permissions: 11 | contents: write 12 | issues: read 13 | #pull-requests: write # to be able to comment on released pull requests 14 | 15 | jobs: 16 | build: 17 | if: github.repository == 'devsecopsmaturitymodel/DevSecOps-MaturityModel' 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | persist-credentials: false # This is important if you have branch protection rules! 23 | - name: Semantic Release 24 | uses: cycjimmy/semantic-release-action@v4 25 | with: 26 | branch: 'master' 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | - name: Get Semantic Release Version 30 | id: get-version 31 | run: echo "::set-output name=version::$(grep -oP '\[\d+\.\d+\.\d+\]' CHANGELOG.md | tr -d '[]')" 32 | 33 | - name: show version 34 | run: | 35 | echo "Semantic Release Version: ${{ steps.get-version.outputs.version }}" 36 | 37 | - name: setup qemu for multi-arch build 38 | uses: docker/setup-qemu-action@v2 39 | with: 40 | platforms: amd64,arm64 41 | - name: setup buildx 42 | uses: docker/setup-buildx-action@v2 43 | - name: Log in to Docker Hub 44 | uses: docker/login-action@v2 45 | with: 46 | #registry: registry.hub.docker.com 47 | username: wurstbrot 48 | password: ${{ secrets.HUB_TOKEN }} 49 | - name: create and push dsomm image 50 | uses: docker/build-push-action@v3 51 | with: 52 | push: true 53 | platforms: linux/amd64,linux/arm64 54 | tags: wurstbrot/dsomm:${{ steps.get-version.outputs.version }},wurstbrot/dsomm:latest 55 | # Commit all changed files back to the repository 56 | - uses: planetscale/ghcommit-action@v0.1.6 57 | with: 58 | commit_message: "🤖 fmt" 59 | repo: ${{ github.repository }} 60 | branch: ${{ github.head_ref || github.ref_name }} 61 | env: 62 | GITHUB_TOKEN: ${{secrets.ACCESS_TOKEN}} 63 | heroku: 64 | if: github.repository == 'devsecopsmaturitymodel/DevSecOps-MaturityModel' && github.event_name == 'push' && github.ref == 'refs/heads/master' 65 | runs-on: ubuntu-latest 66 | steps: 67 | - name: "Check out Git repository" 68 | uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 69 | - name: "Set Heroku app & branch for ${{ github.ref }}" 70 | run: | 71 | echo $GITHUB_REF 72 | if [ "$GITHUB_REF" == "refs/heads/master" ]; then 73 | echo "HEROKU_APP=" >> $GITHUB_ENV 74 | echo "HEROKU_BRANCH=master" >> $GITHUB_ENV 75 | fi 76 | echo "HEROKU_BRANCH=master" >> $GITHUB_ENV 77 | - name: Install Heroku CLI 78 | run: | 79 | curl https://cli-assets.heroku.com/install.sh | sh 80 | - name: "Deploy ${{ github.ref }} to Heroku" 81 | uses: akhileshns/heroku-deploy@v3.13.15 82 | with: 83 | heroku_api_key: ${{ secrets.HEROKU_API_KEY }} 84 | heroku_app_name: "dsomm" 85 | heroku_email: timo.pagel@owasp.org 86 | branch: ${{ env.HEROKU_BRANCH }} 87 | usedocker: true 88 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PR' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | stale-issue-message: > 13 | This issue has been automatically marked as `stale` because it has not had 14 | recent activity. :calendar: It will be _closed automatically_ in one week if no further activity occurs. 15 | stale-pr-message: > 16 | This PR has been automatically marked as `stale` because it has not had 17 | recent activity. :calendar: It will be _closed automatically_ in two weeks if no further activity occurs. 18 | close-issue-message: This issue was closed because it has been stalled for 7 days with no activity. 19 | close-pr-message: This PR was closed because it has been stalled for 20 days with no activity. 20 | days-before-stale: 20 21 | days-before-close: 7 22 | days-before-pr-close: 20 23 | exempt-issue-labels: 'critical,technical debt' 24 | exempt-assignees: wurstbrot 25 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Use Node.js 16.16.0 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: 16.0 15 | - name: Install dependencies 16 | run: npm install --legacy-peer-deps 17 | - name: Test 18 | run: npm test -- --watch=false --browsers=ChromeHeadless 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # angular cache 4 | /.angular/cache 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # IDEs and editors 15 | /.idea 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # IDE - VSCode 24 | .vscode/* 25 | !.vscode/settings.json 26 | 27 | # Cache 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | /yaml-generation/vendor/ 46 | # Generated YAML 47 | /src/assets/YAML/generated/generated.yaml 48 | -------------------------------------------------------------------------------- /.phptidy-config.php: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # angular cache 4 | /.angular/cache 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # IDEs and editors 15 | /.idea 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # IDE - VSCode 24 | .vscode/* 25 | !.vscode/settings.json 26 | 27 | # Cache 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | 46 | /src/assets/YAML/generated/generated.yaml -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "semi": true, 6 | "bracketSpacing": true, 7 | "arrowParens": "avoid", 8 | "trailingComma": "es5", 9 | "bracketSameLine": true, 10 | "printWidth": 80, 11 | "endOfLine": "auto" 12 | } -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branch": "master", 3 | "plugins": [ 4 | [ 5 | "@semantic-release/commit-analyzer", 6 | { 7 | "preset": "angular", 8 | "releaseRules": [ 9 | {"breaking": true, "release": "minor"}, 10 | {"tag": "Breaking", "release": "minor"} 11 | ] 12 | } 13 | ], 14 | "@semantic-release/release-notes-generator", 15 | "@semantic-release/github" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [3.10.0](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/compare/v3.9.0...v3.10.0) (2023-11-10) 2 | 3 | 4 | ### Features 5 | 6 | * decouple yaml-data and application ([45611e8](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/45611e8ee58ec7e9ed8ecf5bb1c54b5bfcb8e885)) 7 | * enhance signing description ([231a5e9](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/231a5e97b66a49b95bbc14147ea43d5ce9646788)) 8 | 9 | # [3.9.0](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/compare/v3.8.0...v3.9.0) (2023-11-09) 10 | 11 | 12 | ### Features 13 | 14 | * enhance signing description ([4546078](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/454607882a909ef5d7c3e5f2f14bcc0a6a43076e)) 15 | 16 | ## [3.5.2](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/compare/v3.5.1...v3.5.2) (2023-11-07) 17 | 18 | 19 | ### Bug Fixes 20 | 21 | * YAML Structure description ([33e50f0](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/33e50f0fb168c5c91b4fedb5a2a7d5e8a4ac0a80)) 22 | 23 | ## [3.5.1](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/compare/v3.5.0...v3.5.1) (2023-11-07) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * YAML ([889422b](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/889422b791cf141838e2ec637406a14d8849ff6a)) 29 | 30 | # [3.5.0](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/compare/v3.4.0...v3.5.0) (2023-11-07) 31 | 32 | 33 | ### Features 34 | 35 | * add WAF ([a98947d](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/a98947da41691e23af255cad8778208db09ccc53)) 36 | 37 | # [3.4.0](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/compare/v3.3.0...v3.4.0) (2023-11-07) 38 | 39 | 40 | ### Features 41 | 42 | * Activity Contexualized Encoding ([f81d3cf](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/commit/f81d3cfedd013b579fac73e1b62bb57dfbc5a7a3)) 43 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for 6 | everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity 7 | and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, 8 | color, religion, or sexual identity and orientation. 9 | 10 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to a positive environment for our community include: 15 | 16 | * Demonstrating empathy and kindness toward other people 17 | * Being respectful of differing opinions, viewpoints, and experiences 18 | * Giving and gracefully accepting constructive feedback 19 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 20 | * Focusing on what is best not just for us as individuals, but for the overall community 21 | 22 | Examples of unacceptable behavior include: 23 | 24 | * The use of sexualized language or imagery, and sexual attention or advances of any kind 25 | * Trolling, insulting or derogatory comments, and personal or political attacks 26 | * Public or private harassment 27 | * Publishing others' private information, such as a physical or email address, without their explicit permission 28 | * Other conduct which could reasonably be considered inappropriate in a professional setting 29 | 30 | ## Enforcement Responsibilities 31 | 32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take 33 | appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, 34 | or harmful. 35 | 36 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, 37 | issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for 38 | moderation decisions when appropriate. 39 | 40 | ## Scope 41 | 42 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing 43 | the community in public spaces. Examples of representing our community include using an official e-mail address, posting 44 | via an official social media account, or acting as an appointed representative at an online or offline event. 45 | 46 | ## Enforcement 47 | 48 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible 49 | for enforcement at 50 | [bjoern.kimminich@owasp.org](mailto:bjoern.kimminich@owasp.org). All complaints will be reviewed and investigated 51 | promptly and fairly. 52 | 53 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 54 | 55 | ## Enforcement Guidelines 56 | 57 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem 58 | in violation of this Code of Conduct: 59 | 60 | ### 1. Correction 61 | 62 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the 63 | community. 64 | 65 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation 66 | and an explanation of why the behavior was inappropriate. A public apology may be requested. 67 | 68 | ### 2. Warning 69 | 70 | **Community Impact**: A violation through a single incident or series of actions. 71 | 72 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including 73 | unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding 74 | interactions in community spaces as well as external channels like social media. Violating these terms may lead to a 75 | temporary or permanent ban. 76 | 77 | ### 3. Temporary Ban 78 | 79 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 80 | 81 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified 82 | period of time. No public or private interaction with the people involved, including unsolicited interaction with those 83 | enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 84 | 85 | ### 4. Permanent Ban 86 | 87 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate 88 | behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 89 | 90 | **Consequence**: A permanent ban from any sort of public interaction within the community. 91 | 92 | ## Attribution 93 | 94 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at 95 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 96 | 97 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 98 | 99 | For answers to common questions about this code of conduct, see the FAQ 100 | at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 101 | [https://www.contributor-covenant.org/translations][translations]. 102 | 103 | [homepage]: https://www.contributor-covenant.org 104 | 105 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 106 | 107 | [Mozilla CoC]: https://github.com/mozilla/diversity 108 | 109 | [FAQ]: https://www.contributor-covenant.org/faq 110 | 111 | [translations]: https://www.contributor-covenant.org/translations 112 | -------------------------------------------------------------------------------- /Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | http_port {$PORT} 3 | } 4 | 5 | :{$PORT}, localhost:{$PORT} { 6 | log stdout 7 | file_server { 8 | root /srv 9 | } 10 | try_files {path} {path}/ /index.html 11 | 12 | header / { 13 | X-Content-Type-Options "nosniff" 14 | X-XSS-Protection "1; mode=block" 15 | X-Robots-Tag "none" 16 | X-Download-Options "noopen" 17 | X-Permitted-Cross-Domain-Policies "none" 18 | Referrer-Policy "no-referrer" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Development.md: -------------------------------------------------------------------------------- 1 | # DevSecOps Maturity Model (DSOMM) 2 | 3 | ## Introduction 4 | 5 | The DevSecOps Maturity Model (DSOMM) is an open-source framework designed to help organizations evaluate and improve their **DevSecOps** practices. 6 | It provides structured **security maturity levels**, recommendations, and automation insights to enable teams to build **secure, efficient, and scalable software**. 7 | 8 | This guide walks you through **setting up the project locally**, making contributions, and submitting a pull request. 9 | 10 | ## **Project Setup** 11 | 12 | ### Development Server 13 | 14 | The DSOMM is based [Angular](https://angular.dev/) and uses npm for package management. 15 | 16 | - If you have not yet installed npm or the Angular command line tools, install them now. First [NodeJS](https://nodejs.org/en/download) (which provides npm), then Angular: 17 | 18 | ```bash 19 | npm install -g @angular/cli 20 | ``` 21 | 22 | - Clone the DSOMM repo 23 | 24 | ```bash 25 | git clone https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel.git 26 | ``` 27 | 28 | - Change directory to DSOMM 29 | 30 | ```bash 31 | cd DevSecOps-MaturityModel 32 | ``` 33 | 34 | - Install Dependencies 35 | 36 | ```bash 37 | npm install 38 | ``` 39 | 40 | - **NB!** The DSOMM activities are maintained separately. Download the `generated.yaml` and put it in the required folder 41 | 42 | ```bash 43 | curl https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/main/src/assets/YAML/generated/generated.yaml -o src/assets/YAML/generated/generated.yaml 44 | ``` 45 | 46 | - Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 47 | 48 | ## Code Scaffolding 49 | 50 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 51 | 52 | ## Build 53 | 54 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 55 | 56 | ## Running Unit Tests 57 | 58 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 59 | 60 | ## Coding Style Conventions 61 | 62 | - We follow the coding style defined by [ESLint](https://eslint.org/). 63 | - We also use [Prettier](https://prettier.io/docs/en/index.html) as our opinionated code formatter. 64 | - To validate the schemas of the DSOMM yaml files in the IDE, it is recommended to use the VS Code extension [redhat.vscode-yaml](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml). The schemas are stored in `/src/assets/YAML/schemas` 65 | 66 | ### Running Linter 67 | 68 | Run `ng lint` to run the linter from the command line. 69 | If you want to lint only a specific component, use: 70 | 71 | ```bash 72 | ng lint --lint-file-patterns .\src\app\component\xxxxxx\ 73 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18.7-alpine AS build 2 | 3 | WORKDIR /usr/src/app 4 | COPY package.json package-lock.json ./ 5 | RUN apk add --upgrade python3 build-base \ 6 | && npm install 7 | COPY . . 8 | RUN npm run build 9 | 10 | FROM wurstbrot/dsomm-yaml-generation as yaml 11 | 12 | FROM caddy 13 | ENV PORT 8080 14 | 15 | COPY Caddyfile /etc/caddy/Caddyfile 16 | COPY --from=build ["/usr/src/app/dist/dsomm/", "/srv"] 17 | COPY --from=yaml ["/var/www/html/src/assets/YAML/generated/generated.yaml", "/srv/assets/YAML/generated/generated.yaml"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | From a startup to a multinational corporation the software development industry is currently dominated by agile frameworks and product teams and as part of it DevOps strategies. It has been observed that during the implementation, security aspects are usually neglected or are at least not sufficient taken account of. It is often the case that standard safety requirements of the production environment are not utilized or applied to the build pipeline in the continuous integration environment with containerization or concrete docker. Therefore, the docker registry is often not secured which might result in the theft of the entire company’s source code. 4 | 5 | The OWASP DevSecOps Maturity Model provides opportunities to harden DevOps strategies and shows how these can be prioritized. 6 | 7 | With the help of DevOps strategies security can also be enhanced. For example, each component such as application libraries and operating system libraries in docker images can be tested for known vulnerabilities. 8 | 9 | Attackers are intelligent and creative, equipped with new technologies and purpose. Under the guidance of the forward-looking DevSecOps Maturity Model, appropriate principles and measures are at hand implemented which counteract the attacks. 10 | 11 | # Usage 12 | 13 | Go to https://dsomm.owasp.org. 14 | 15 | * _matrix_ shows the dimensions, subdimensions and activities are described. 16 | * _Implementation Levels_ can be used to show the current implementation level by clicking on the specific activities which have been performed (it is recommended to use a gitops-like flow) 17 | * _Mappings_ Shows mappings to other standards and provides the ability to download an excel sheet 18 | * _Usage_ describes how to use DSOMM 19 | 20 | In this [video](https://www.youtube.com/watch?v=tX9RHZ_O5NU) Timo Pagel describes different strategic approaches for your secure DevOps strategy. The use OWASP DSOMM in combination with [OWASP SAMM](https//owaspsamm.org) is explained. 21 | 22 | In case you have evidence or review questions to gather evidence, you can add the attribute "evidence" to an activity which will be attached to an activity to provide it to your CISO or your customer's CISO. 23 | You can switch on to show open TODO's for evidence by changing IS_SHOW_EVIDENCE_TODO to true 'bib.php' `define(IS_SHOW_EVIDENCE_TODO, true);` 24 | 25 | This page uses the Browser's localStorage to store the state of the circular headmap. 26 | 27 | # Changes 28 | Changes to the application are displayed at the release page of [DevSecOps-MaturityModel](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/releases). 29 | 30 | Changes to the maturity model content are displayed at the release page of [DevSecOps-MaturityModel-data](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/releases). 31 | 32 | # Community 33 | Join #dsomm in [OWASP Slack](https://owasp.slack.com/join/shared_invite/zt-g398htpy-AZ40HOM1WUOZguJKbblqkw#/). 34 | Create issues or even better Pull Requests in [github](https://github.com/wurstbrot/DevSecOps-MaturityModel/). 35 | 36 | # Slides and talks 37 | * [Video: OWASP (DevSecOps) Projects, 2021-04-28, OWASP Stammtisch Frankfurt](https://www.youtube.com/watch?v=8webiYnF56A) 38 | * [Video: DSOMM Enhancement Workshop at Open Security Summit, 2021-04-16](https://youtu.be/H2BA6gaeKBE) 39 | * [Video: Strategic Usage of the OWASP Software Assurance Maturity Model and the OWASP DevSecOps Maturity Model, OWASP Jakarta](https://m.youtube.com/watch?v=lLMLGIzl56M) 40 | * [Slides: DSOMM Overview](https://docs.google.com/presentation/d/1eQcE_AsR1g6uOVf3B2Ehh1g0cHvPknkdLY4BzMYatSw/edit?usp=sharing) 41 | * [Video: GitHub practical DSOMM snippet on twitch](https://www.twitch.tv/githubenterprise/clip/EsteemedTriumphantMinkFailFish) 42 | * [Blog: GitHub on DSOMM](https://github.blog/2020-08-06-achieving-devsecops-maturity-with-a-developer-first-community-driven-approach/) 2020 43 | * [Video: Benutzung vom OWASP DevSecOps Maturity Model (German)](https://vimeo.com/456523229) 44 | * [Online: OWASP DevSecOps Maturity Model - Culture (German)](https://www.meetup.com/de-DE/Breaking-Agile/) 2020-08-25 45 | * [Video: Usage of the OWASP DevSecOps Maturity Model](https://www.youtube.com/watch?v=tX9RHZ_O5NU), [OWASP Ottawa Chapter](https://www.meetup.com/de-DE/OWASP-Ottawa/events/272355636/), 2020-08-17 46 | * [Continuous Application Security Testing for Enterprise](https://docs.google.com/presentation/d/1dAewXIHgBEKHKwBPpM5N_G2eM6PRpduoGJrp6R6pNUI/edit?usp=sharing), DevOps Meetup Hamburg, 2019-09-26 47 | * [DevSecOps Maturity Model](https://docs.google.com/presentation/d/1zF7c_0cPYBO7LHcLNtEApQBB_qJugXgRQUyiwBKKtKk/edit?usp=sharing), Open Security Summit, near London, 2018 48 | * [Security in DevOps-Strategies](https://www.youtube.com/watch?v=gWjGWebWahE&t=448s), 28.09.2017, Hamburg, Germany 49 | * [DevSecOps Maturity Model](https://docs.google.com/presentation/d/1rrbyXqxy3LXAJNPFrVH99mj_BNaJKymMsXZItYArWEM/edit?usp=sharing), 2017 50 | 51 | # Assessment 52 | 53 | In case you would like to perform a DevSecOps assessment, the following tools are available: 54 | 55 | * Usage of the applicaton in a `container`. 56 | * Development of an export to [OWASP Maturity Models](https://github.com/OWASP/Maturity-Models) (recommended for assessments with a lot of teams) 57 | * Creation of your excel sheet (not recommended, you want to use DevOps, don't even try!) 58 | 59 | ## Container 60 | 61 | 1. Install [Docker](https://www.docker.com) 62 | 2. Run `docker pull wurstbrot/dsomm:latest && docker run --rm -p 8080:8080 wurstbrot/dsomm:latest` 63 | 3. Browse to (on macOS and Windows browse to if you are using docker-machine instead 64 | of the native docker installation) 65 | 66 | For customized DSOMM, take a look at https://github.com/wurstbrot/DevSecOps-MaturityModel-custom. 67 | 68 | You can download your current state from the circular heatmap and mount it again via 69 | 70 | ```bash 71 | wget https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/main/src/assets/YAML/generated/generated.yaml # or go to /circular-heatmap and download edited yaml (bottom right) 72 | docker run -p 8080:8080 -v /tmp/generated.yaml:/srv/assets/YAML/generated/generated.yaml wurstbrot/dsomm:latest 73 | ``` 74 | 75 | . 76 | 77 | This approach also allows teams to perform self assessment with changes tracked in a repository. 78 | 79 | ## Amazon EC2 Instance 80 | 81 | 1. In the _EC2_ sidenav select _Instances_ and click _Launch Instance_ 82 | 2. In _Step 1: Choose an Amazon Machine Image (AMI)_ choose an _Amazon 83 | Linux AMI_ or _Amazon Linux 2 AMI_ 84 | 3. In _Step 3: Configure Instance Details_ unfold _Advanced Details_ and 85 | copy the script below into _User Data_ 86 | 4. In _Step 6: Configure Security Group_ add a _Rule_ that opens port 80 87 | for HTTP 88 | 5. Launch your instance 89 | 6. Browse to your instance's public DNS 90 | 91 | ```bash 92 | #!/bin/bash 93 | service docker start 94 | docker run -d -p 80:8080 wurstbrot/dsomm:latest 95 | ``` 96 | 97 | ## Generating the `generated.yaml` File 98 | 99 | The `generated.yaml` file is dynamically created during the build process. If you don’t see this file after setup, follow these steps to generate it: 100 | 101 | **1. Clone the Required Repository:** 102 | The `generated.yaml` file is built via the DevSecOps-MaturityModel-data repository. Make sure you have cloned and set it up correctly. 103 | 104 | **2. Run the Build Command:** 105 | Navigate to the project directory and run the following command: 106 | - *Using npm:* 107 | 108 | ```sh 109 | npm run build 110 | ```` 111 | 112 | - *Using yarn:* 113 | 114 | ```sh 115 | yarn build 116 | ``` 117 | 118 | *If the file is missing, ensure all dependencies are installed and that you have the correct access to the `DevSecOps-MaturityModel-data` repository.* 119 | 120 | ## Activity Definitions 121 | The definition of the activities are in the [data-repository](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data). 122 | 123 | ## Teams and Groups 124 | To customize these teams, you can create your own [meta.yaml](src/assets/meta.yaml) file with your unique team definitions. 125 | 126 | Assessments within the framework can be based on either a team or a specific application, which can be referred to as the context. Depending on how you define the context or teams, you may want to group them together. 127 | 128 | Here are a couple of examples to illustrate this, in breakers the DSOMM word: 129 | - Multiple applications (teams) can belong to a single overarching team (application). 130 | - Multiple teams (teams) can belong to a larger department (group). 131 | 132 | Feel free to create your own [meta.yaml](src/assets/meta.yaml) file to tailor the framework to your specific needs and mount it in your environment (e.g. kubernetes or docker). 133 | Here is an example to start docker with customized meta.yaml: 134 | ``` 135 | # Customized meta.yaml 136 | cp src/assets/YAML/meta.yaml . 137 | docker run -v $(pwd)/meta.yaml:/srv/assets/YAML/meta.yaml -p 8080:8080 wurstbrot/dsomm 138 | 139 | # Customized meta.yaml and generated.yaml 140 | cp src/assets/YAML/meta.yaml . 141 | cp $(pwd)/src/assets/YAML/generated/generated.yaml . 142 | docker run -v $(pwd)/meta.yaml:/srv/assets/YAML/meta.yaml -v $(pwd)/generated.yaml:/srv/assets/YAML/generated/generated.yaml -p 8080:8080 wurstbrot/dsomm 143 | ``` 144 | 145 | In the corresponding [dimension YAMLs](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/tree/main/src/assets/YAML/default), use: 146 | ``` 147 | [...] 148 | teamsImplemented: 149 | Default: false 150 | B: true 151 | C: true 152 | teamsEvidence: 153 | B: All team members completed OWASP Secure Coding Dojo training on 2025-01-11. 154 | C: | 155 | The pentest report from 2025 has been split into Jira tasks under 156 | [TODO-123](https://jira.example.com/issues/TODO-123). 157 | 158 | _2025-04-01:_ All fixes of **critical** findings are deployed to production. 159 | ``` 160 | The `|` is yaml syntax to indicate that the evidence spans multiple lines. Markdown 161 | syntax can be used. The evidence is currently visible on the activity from the Matrix page. 162 | 163 | # Back link 164 | 165 | - [OWASP DevSecOps maturity model page](https://dsomm.timo-pagel.de/) 166 | - [OWASP DevSecOps project page](https://owasp.org/www-project-devsecops-maturity-model/) 167 | - [OWASP](https://owasp.org) 168 | 169 | # Your help is needed to perform 170 | 171 | * Adding a manual on how to use DSOMM 172 | * Integration of Incident Response 173 | * DevSecOps Toolchain Categorization 174 | * App Sec Maturity Models Mapping 175 | * CAMS Categorization 176 | * Adding assessment questions 177 | 178 | # Multilanguage support 179 | Multilanguage support is not given currently and not planned. 180 | 181 | # Sponsors 182 | 183 | [![Timo Pagel IT-Consulting](https://raw.githubusercontent.com/DefectDojo/Documentation/master/doc/img/timo-pagel-logo.png)](https://pagel.pro) 184 | 185 | [![Apprio Inc](https://github.com/wurstbrot/DevSecOps-MaturityModel/raw/master-old/assets/images/Apiiro_black_logo.png)](https://apiiro.com/) 186 | 187 | [![Heroku (hosting)](https://github.com/wurstbrot/DevSecOps-MaturityModel/raw/master/src/assets/images/sponsors/heroku.png)](https://www.heroku.com/open-source-credit-program) 188 | 189 | # Donations 190 | 191 | If you are using the model or you are inspired by it, want to help but don't want to create pull requests? You can donate at the [OWASP Project Wiki Page](https://owasp.org/donate/?reponame=www-project-devsecops-maturity-model&title=OWASP+Devsecops+Maturity+Model). Donations might be used for the design of logos/images/design or travels. 192 | 193 | # License 194 | 195 | This program is free software: you can redistribute it and/or modify it under the terms of the [GPL 3](https://www.gnu.org/licenses/) license. 196 | 197 | The intellectual property (content in the _data_ folder) is licensed under Attribution-ShareAlike. 198 | An example attribution by changing the content: 199 | > This work is based on the [OWASP DevSecOps Maturity Model](https://dsomm.timo-pagel.de). 200 | 201 | The OWASP DevSecOps Maturity Model and any contributions are Copyright © by Timo Pagel 2017-2022. 202 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "DSOMM": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:application": { 10 | "strict": true 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/dsomm", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "allowedCommonJsDependencies": ["yamljs"], 26 | "assets": [ 27 | "src/favicon.ico", 28 | "src/assets" 29 | ], 30 | "styles": [ 31 | "src/custom-theme.scss", 32 | "src/styles.css" 33 | ], 34 | "scripts": [] 35 | }, 36 | "configurations": { 37 | "production": { 38 | "budgets": [ 39 | { 40 | "type": "initial", 41 | "maximumWarning": "1mb", 42 | "maximumError": "3mb" 43 | }, 44 | { 45 | "type": "anyComponentStyle", 46 | "maximumWarning": "2kb", 47 | "maximumError": "4kb" 48 | } 49 | ], 50 | "fileReplacements": [ 51 | { 52 | "replace": "src/environments/environment.ts", 53 | "with": "src/environments/environment.prod.ts" 54 | } 55 | ], 56 | "outputHashing": "all" 57 | }, 58 | "development": { 59 | "buildOptimizer": false, 60 | "optimization": false, 61 | "vendorChunk": true, 62 | "extractLicenses": false, 63 | "sourceMap": true, 64 | "namedChunks": true 65 | } 66 | }, 67 | "defaultConfiguration": "production" 68 | }, 69 | "serve": { 70 | "builder": "@angular-devkit/build-angular:dev-server", 71 | "configurations": { 72 | "production": { 73 | "browserTarget": "DSOMM:build:production" 74 | }, 75 | "development": { 76 | "browserTarget": "DSOMM:build:development" 77 | } 78 | }, 79 | "defaultConfiguration": "development" 80 | }, 81 | "extract-i18n": { 82 | "builder": "@angular-devkit/build-angular:extract-i18n", 83 | "options": { 84 | "browserTarget": "DSOMM:build" 85 | } 86 | }, 87 | "test": { 88 | "builder": "@angular-devkit/build-angular:karma", 89 | "options": { 90 | "main": "src/test.ts", 91 | "polyfills": "src/polyfills.ts", 92 | "tsConfig": "tsconfig.spec.json", 93 | "karmaConfig": "karma.conf.js", 94 | "assets": [ 95 | "src/favicon.ico", 96 | "src/assets" 97 | ], 98 | "styles": [ 99 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 100 | "src/styles.css" 101 | ], 102 | "scripts": [] 103 | } 104 | }, 105 | "lint": { 106 | "builder": "@angular-eslint/builder:lint", 107 | "options": { 108 | "lintFilePatterns": [ 109 | "src/**/*.ts", 110 | "src/**/*.html" 111 | ] 112 | } 113 | } 114 | } 115 | } 116 | }, 117 | "defaultProject": "DSOMM", 118 | "cli": { 119 | "defaultCollection": "@angular-eslint/schematics" 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/dsomm'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dsomm", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "heroku-postbuild": "ng build --aot --prod" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^13.0.0", 16 | "@angular/cdk": "^13.0.0", 17 | "@angular/cli": "^13.0.0", 18 | "@angular/common": "^13.0.0", 19 | "@angular/compiler": "^13.0.0", 20 | "@angular/core": "^13.0.0", 21 | "@angular/forms": "^13.0.0", 22 | "@angular/localize": "^13.0.0", 23 | "@angular/material": "^13.0.0", 24 | "@angular/platform-browser": "^13.0.0", 25 | "@angular/platform-browser-dynamic": "^13.0.0", 26 | "@angular/router": "^13.0.0", 27 | "@ngneat/until-destroy": "^10.0.0-beta.0", 28 | "d3": "^7.5.0", 29 | "js-yaml": "^4.1.0", 30 | "markdown-it": "^13.0.1", 31 | "rxjs": "~7.5.0", 32 | "tslib": "^2.3.0", 33 | "xlsx": "^0.18.5", 34 | "yamljs": "^0.3.0", 35 | "zone.js": "~0.11.4" 36 | }, 37 | "devDependencies": { 38 | "@angular-devkit/build-angular": "^13.0.0", 39 | "@angular-eslint/builder": "^13.0.0", 40 | "@angular-eslint/eslint-plugin": "^13.0.0", 41 | "@angular-eslint/eslint-plugin-template": "^13.0.0", 42 | "@angular-eslint/schematics": "^13.0.0", 43 | "@angular-eslint/template-parser": "^13.0.0", 44 | "@angular/compiler-cli": "^13.0.0", 45 | "@types/d3": "^7.4.0", 46 | "@types/jasmine": "~3.10.0", 47 | "@types/js-yaml": "^4.0.5", 48 | "@types/markdown-it": "^12.2.3", 49 | "@types/node": "^12.11.1", 50 | "@types/yamljs": "^0.2.31", 51 | "@typescript-eslint/eslint-plugin": "5.27.1", 52 | "@typescript-eslint/parser": "5.27.1", 53 | "eslint": "^8.17.0", 54 | "eslint-config-prettier": "^8.5.0", 55 | "eslint-config-standard-with-typescript": "^22.0.0", 56 | "eslint-plugin-import": "^2.26.0", 57 | "eslint-plugin-n": "^15.2.5", 58 | "eslint-plugin-prettier": "^4.2.1", 59 | "eslint-plugin-promise": "^6.0.1", 60 | "jasmine-core": "~4.0.0", 61 | "karma": "~6.3.0", 62 | "karma-chrome-launcher": "~3.1.0", 63 | "karma-coverage": "~2.1.0", 64 | "karma-jasmine": "~4.0.0", 65 | "karma-jasmine-html-reporter": "~1.7.0", 66 | "prettier": "^2.7.1", 67 | "prettier-eslint": "^15.0.1", 68 | "qs": "^6.11.0", 69 | "typescript": "^4.6.4" 70 | }, 71 | "browser": { 72 | "fs": false, 73 | "path": false, 74 | "os": false 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { Component, NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { AboutUsComponent } from './component/about-us/about-us.component'; 4 | import { UserdayComponent } from './component/userday/userday.component'; 5 | import { CircularHeatmapComponent } from './component/circular-heatmap/circular-heatmap.component'; 6 | import { MappingComponent } from './component/mapping/mapping.component'; 7 | import { MatrixComponent } from './component/matrix/matrix.component'; 8 | import { ActivityDescriptionComponent } from './component/activity-description/activity-description.component'; 9 | import { UsageComponent } from './component/usage/usage.component'; 10 | import { TeamsComponent } from './component/teams/teams.component'; 11 | 12 | const routes: Routes = [ 13 | { path: '', component: MatrixComponent }, 14 | { path: 'circular-heatmap', component: CircularHeatmapComponent }, 15 | { path: 'matrix', component: MatrixComponent }, 16 | { path: 'activity-description', component: ActivityDescriptionComponent }, 17 | { path: 'mapping', component: MappingComponent }, 18 | { path: 'usage', redirectTo: 'usage/' }, 19 | { path: 'usage/:page', component: UsageComponent }, 20 | { path: 'teams', component: TeamsComponent }, 21 | { path: 'about', component: AboutUsComponent }, 22 | { path: 'userday', component: UserdayComponent }, 23 | ]; 24 | 25 | @NgModule({ 26 | imports: [RouterModule.forRoot(routes)], 27 | exports: [RouterModule], 28 | }) 29 | export class AppRoutingModule {} 30 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | 2 | .main-container { 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | .sidenav-content { 8 | display: flex; 9 | padding: 10px; 10 | justify-content: space-between; 11 | } 12 | 13 | .sidenav-menu { 14 | padding: 20px; 15 | } 16 | 17 | .menu-close { 18 | position: absolute; 19 | right: 0; 20 | top: 2px; 21 | background: transparent; 22 | border: 0; 23 | color: #ddd; 24 | } 25 | 26 | .github-fork-ribbon:before { 27 | background-color: #333; 28 | } 29 | 30 | @media only screen and (max-width: 750px) { 31 | .github-fork-ribbon { 32 | display: none; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | 12 | 13 | 14 |
15 | 18 | Fork me on GitHub 25 |
26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | let app: AppComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [RouterTestingModule], 12 | declarations: [AppComponent], 13 | }).compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AppComponent); 18 | app = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create the app', () => { 23 | expect(app).toBeTruthy(); 24 | }); 25 | 26 | it(`should have as title 'DSOMM'`, () => { 27 | expect(app.title).toEqual('DSOMM'); 28 | }); 29 | 30 | it('check for fork me on github ribbon generation', () => { 31 | const fixture = TestBed.createComponent(AppComponent); 32 | const HTMLElement: HTMLElement = fixture.nativeElement; 33 | var divTag = HTMLElement.querySelector('div')!; 34 | var aTag = divTag.querySelector('a')!; 35 | //console.log(aTag); 36 | expect(aTag.textContent).toEqual('Fork me on GitHub'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ThemeService } from './service/theme.service'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'], 8 | }) 9 | export class AppComponent implements OnInit { 10 | title = 'DSOMM'; 11 | menuIsOpen: boolean = true; 12 | 13 | constructor(private themeService: ThemeService) { 14 | this.themeService.initTheme(); 15 | } 16 | 17 | ngOnInit(): void { 18 | let menuState: string | null = localStorage.getItem('state.menuIsOpen'); 19 | if (menuState === 'false') { 20 | setTimeout(() => { 21 | this.menuIsOpen = false; 22 | }, 600); 23 | } 24 | } 25 | 26 | toggleMenu(): void { 27 | this.menuIsOpen = !this.menuIsOpen; 28 | localStorage.setItem('state.menuIsOpen', this.menuIsOpen.toString()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { AppRoutingModule } from './app-routing.module'; 6 | import { AppComponent } from './app.component'; 7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 8 | import { MaterialModule } from './material/material.module'; 9 | import { LogoComponent } from './component/logo/logo.component'; 10 | import { MatrixComponent } from './component/matrix/matrix.component'; 11 | import { SidenavButtonsComponent } from './component/sidenav-buttons/sidenav-buttons.component'; 12 | import { TopHeaderComponent } from './component/top-header/top-header.component'; 13 | import { ActivityDescriptionComponent } from './component/activity-description/activity-description.component'; 14 | import { ymlService } from './service/yaml-parser/yaml-parser.service'; 15 | import { HttpClientModule } from '@angular/common/http'; 16 | import { CircularHeatmapComponent } from './component/circular-heatmap/circular-heatmap.component'; 17 | import { MappingComponent } from './component/mapping/mapping.component'; 18 | import { ReadmeToHtmlComponent } from './component/readme-to-html/readme-to-html.component'; 19 | import { UsageComponent } from './component/usage/usage.component'; 20 | import { UserdayComponent } from './component/userday/userday.component'; 21 | import { AboutUsComponent } from './component/about-us/about-us.component'; 22 | import { DependencyGraphComponent } from './component/dependency-graph/dependency-graph.component'; 23 | import { TeamsComponent } from './component/teams/teams.component'; 24 | import { ToStringValuePipe } from './pipe/to-string-value.pipe'; 25 | import { ModalMessageComponent } from './component/modal-message/modal-message.component'; 26 | import { 27 | MatDialogModule, 28 | MAT_DIALOG_DATA, 29 | MatDialogRef, 30 | } from '@angular/material/dialog'; 31 | 32 | @NgModule({ 33 | declarations: [ 34 | AppComponent, 35 | LogoComponent, 36 | MatrixComponent, 37 | SidenavButtonsComponent, 38 | TopHeaderComponent, 39 | ActivityDescriptionComponent, 40 | CircularHeatmapComponent, 41 | MappingComponent, 42 | ReadmeToHtmlComponent, 43 | UsageComponent, 44 | AboutUsComponent, 45 | DependencyGraphComponent, 46 | TeamsComponent, 47 | ToStringValuePipe, 48 | UserdayComponent, 49 | ModalMessageComponent, 50 | ], 51 | imports: [ 52 | BrowserModule, 53 | AppRoutingModule, 54 | BrowserAnimationsModule, 55 | MaterialModule, 56 | MatDialogModule, 57 | ReactiveFormsModule, 58 | HttpClientModule, 59 | ], 60 | providers: [ 61 | ymlService, 62 | ModalMessageComponent, 63 | { provide: MAT_DIALOG_DATA, useValue: {} }, 64 | { provide: MatDialogRef, useValue: { close: (dialogResult: any) => {} } }, 65 | ], 66 | bootstrap: [AppComponent], 67 | }) 68 | export class AppModule {} 69 | -------------------------------------------------------------------------------- /src/app/component/about-us/about-us.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/app/component/about-us/about-us.component.css -------------------------------------------------------------------------------- /src/app/component/about-us/about-us.component.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /src/app/component/about-us/about-us.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutUsComponent } from './about-us.component'; 4 | 5 | describe('AboutUsComponent', () => { 6 | let component: AboutUsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [AboutUsComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(AboutUsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/component/about-us/about-us.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-about-us', 5 | templateUrl: './about-us.component.html', 6 | styleUrls: ['./about-us.component.css'], 7 | }) 8 | export class AboutUsComponent { 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/component/activity-description/activity-description.component.css: -------------------------------------------------------------------------------- 1 | .content-box { 2 | margin: 20px; 3 | width: 95%; 4 | /*height: 100%;*/ 5 | /*background-color: paleturquoise;*/ 6 | } 7 | 8 | .mat-form-field + .mat-form-field { 9 | margin-left: 8px; 10 | } 11 | 12 | .mat-raised-button { 13 | margin-right: 8px; 14 | } 15 | .teams-implemented-list { 16 | list-style: none; 17 | } -------------------------------------------------------------------------------- /src/app/component/activity-description/activity-description.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{ currentActivity.dimension }} -> {{ currentActivity.subDimension }}: 4 | {{ currentActivity.activityName }} 5 |

6 | 7 |
8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | UUID 17 | 18 | 19 |

20 |
21 | 22 | 23 | 24 | 25 | Description 26 | 27 | 28 |

29 |
30 | 31 | 32 | 33 | 34 | Risk 35 | 36 | 37 |

38 |
39 | 40 | 41 | 42 | 43 | Measure 44 | 45 | 46 |

47 |
48 | 49 | 50 | 51 | 52 | Implementation Guide 53 | 54 | 55 |

58 |
59 | 60 | 61 | 62 | 63 | Difficulty of Implementation 64 | 65 | 66 |

67 | Knowledge: {{ this.KnowledgeLabels[this.currentActivity.knowledge] }} 68 |

69 |

Time: {{ this.GeneralLabels[this.currentActivity.time] }}

70 |

Resources: {{ this.GeneralLabels[this.currentActivity.resources] }}

71 |
72 | 73 | 74 | 75 | 76 | Usefulness 77 | 78 | 79 |

{{ this.GeneralLabels[this.currentActivity.usefulness] }}

80 |
81 | 82 | 83 | 84 | 85 | Teams Evidence 86 | 87 | 88 | 89 | 92 | 93 | 94 | 95 | 96 | 97 |

98 |
99 |
100 |
101 | 102 | 103 | 104 | 105 | Assessment 106 | 107 | 108 |

109 |
110 | 111 | 112 | 113 | 114 | Implementation 115 | 116 | 117 |

118 | 119 | 121 | 122 | 123 | 124 | 125 | 126 |

127 | 128 | 129 | 130 | 131 | Tags 132 | 133 | 134 |

    135 |
  • 136 |
137 |
138 | 139 | 140 | 141 | URL 142 | 143 | 144 |

145 |
146 | 147 | 148 | 149 | Description 150 | 151 | 152 |

153 |
154 |
155 |

156 | 157 | 158 |

159 | 160 | 161 | 162 | 163 | 164 | References 165 | 166 | 167 |

168 | 169 | 170 | 171 | 172 | {{ SAMMVersion }} 173 | 174 | 175 |

    176 |
  • {{ samm }}
  • 177 |
178 |
179 | 180 | 181 | 182 | {{ ISOVersion }} 183 | 184 | 185 |
    186 |
  • {{ iso }}
  • 187 |
188 |
189 | 190 | 191 | 192 | {{ ISO22Version }} 193 | 194 | 195 |
    196 |
  • {{ iso22 }}
  • 197 |
198 |
199 | 200 | 201 | 202 | {{ openCREVersion }} 203 | 204 | 205 | 212 | 213 | 214 |

215 | 216 | 217 | 218 | 219 | 220 | Implemented By 221 | 222 | 223 |
229 | 230 |
    231 |
  • 232 | {{ item.key }}:{{ item.value }} 234 |
  • 235 |
236 |
237 | teamsImplemented variable not present
239 |

Not Implemented

240 |

Implemented

241 |
242 |
243 | 244 | 245 | 246 | Depends on 247 | 248 | 249 | 255 | 256 | 257 | 258 | 259 | 260 | Comments 261 | 262 | 263 |

264 |
265 | 266 | 267 | 268 | 269 | 270 | Tags 271 | 272 | 273 |

274 |
275 |
276 | -------------------------------------------------------------------------------- /src/app/component/activity-description/activity-description.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHandler } from '@angular/common/http'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { RouterTestingModule } from '@angular/router/testing'; 4 | import { sample } from 'rxjs'; 5 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 6 | 7 | import { ActivityDescriptionComponent } from './activity-description.component'; 8 | 9 | describe('ActivityDescriptionComponent', () => { 10 | let component: ActivityDescriptionComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | providers: [ymlService, HttpClient, HttpHandler], 16 | imports: [RouterTestingModule], 17 | declarations: [ActivityDescriptionComponent], 18 | }).compileComponents(); 19 | }); 20 | 21 | beforeEach(() => { 22 | fixture = TestBed.createComponent(ActivityDescriptionComponent); 23 | component = fixture.componentInstance; 24 | fixture.detectChanges(); 25 | }); 26 | 27 | it('should create', () => { 28 | expect(component).toBeTruthy(); 29 | }); 30 | 31 | it('check if header is being generated', () => { 32 | const testDimension = 'Sample Dimension'; 33 | const testSubDimension = 'Sample subDimension'; 34 | component.currentActivity.dimension = testDimension; 35 | component.currentActivity.subDimension = testSubDimension; 36 | fixture.detectChanges(); 37 | const HTMLElement: HTMLElement = fixture.nativeElement; 38 | const heading = HTMLElement.querySelector('h1')!; 39 | expect(heading.textContent).toContain(testDimension); 40 | expect(heading.textContent).toContain(testSubDimension); 41 | }); 42 | 43 | it('check if UUID is being generated', () => { 44 | const testUUID = '00000000-0000-0000-0000-000000000000'; 45 | component.currentActivity.uuid = testUUID; 46 | fixture.detectChanges(); 47 | const HTMLElement: HTMLElement = fixture.nativeElement; 48 | const contentDisplayedinParagraphTag = HTMLElement.querySelector('#uuid')!; 49 | expect(contentDisplayedinParagraphTag.textContent).toContain(testUUID); 50 | }); 51 | 52 | it('check if description is being generated', () => { 53 | const testDescription = 'Sample Description'; 54 | component.currentActivity.description = testDescription; 55 | fixture.detectChanges(); 56 | const HTMLElement: HTMLElement = fixture.nativeElement; 57 | const contentDisplayedinParagraphTag = 58 | HTMLElement.querySelector('#description')!; 59 | expect(contentDisplayedinParagraphTag.textContent).toContain( 60 | testDescription 61 | ); 62 | }); 63 | 64 | it('check if risk is being generated', () => { 65 | const testRisk = 'Sample Risk'; 66 | component.currentActivity.risk = testRisk; 67 | fixture.detectChanges(); 68 | const HTMLElement: HTMLElement = fixture.nativeElement; 69 | const contentDisplayedinParagraphTag = HTMLElement.querySelector('#risk')!; 70 | expect(contentDisplayedinParagraphTag.textContent).toContain(testRisk); 71 | }); 72 | 73 | it('check if measure is being generated', () => { 74 | const testMeasure = 'Sample Measure'; 75 | component.currentActivity.measure = testMeasure; 76 | fixture.detectChanges(); 77 | const HTMLElement: HTMLElement = fixture.nativeElement; 78 | const contentDisplayedinParagraphTag = 79 | HTMLElement.querySelector('#measure')!; 80 | expect(contentDisplayedinParagraphTag.textContent).toContain(testMeasure); 81 | }); 82 | 83 | it('check if implementation guide is being generated', () => { 84 | const testImplementationGuide = 'Sample Implementation Guide'; 85 | component.currentActivity.implementatonGuide = testImplementationGuide; 86 | fixture.detectChanges(); 87 | const HTMLElement: HTMLElement = fixture.nativeElement; 88 | const contentDisplayedinParagraphTag = HTMLElement.querySelector( 89 | '#implementatonGuide' 90 | )!; 91 | expect(contentDisplayedinParagraphTag.textContent).toContain( 92 | testImplementationGuide 93 | ); 94 | }); 95 | 96 | it('check if evidence is being generated', () => { 97 | const testEvidence = { A: 'Sample Evidence', Default: 'Sample evidence 2' }; 98 | component.currentActivity.teamsEvidence = testEvidence; 99 | fixture.detectChanges(); 100 | const HTMLElement: HTMLElement = fixture.nativeElement; 101 | const parentElement = HTMLElement.querySelectorAll('#teamsEvidence')!; 102 | console.log('parentElement', parentElement[1].textContent); 103 | const lengthOfObject = Object.keys(testEvidence).length; 104 | for (var i = 0; i > lengthOfObject; i++) 105 | expect(parentElement[i].textContent).toContain( 106 | Object.keys(testEvidence)[i] + Object.values(testEvidence)[i] 107 | ); 108 | }); 109 | 110 | it('check if assessment is being generated', () => { 111 | const testAssessment = 'Sample Assessment'; 112 | component.currentActivity.assessment = testAssessment; 113 | fixture.detectChanges(); 114 | const HTMLElement: HTMLElement = fixture.nativeElement; 115 | const contentDisplayedinParagraphTag = 116 | HTMLElement.querySelector('#assessment')!; 117 | expect(contentDisplayedinParagraphTag.textContent).toContain( 118 | testAssessment 119 | ); 120 | }); 121 | 122 | it('check if comments is being generated', () => { 123 | const testComments = 'Sample Comments'; 124 | component.currentActivity.comments = testComments; 125 | fixture.detectChanges(); 126 | const HTMLElement: HTMLElement = fixture.nativeElement; 127 | const contentDisplayedinParagraphTag = 128 | HTMLElement.querySelector('#comments')!; 129 | expect(contentDisplayedinParagraphTag.textContent).toContain(testComments); 130 | }); 131 | 132 | it('check if references is being generated', () => { 133 | const testSAMM = [' Sample SAMM ']; 134 | const testISO = [' Sample ISO']; 135 | const testISO22 = [' Sample ISO22']; 136 | const uuid = 'abcd'; // for openCRE 137 | 138 | component.currentActivity.samm = testSAMM; 139 | component.currentActivity.iso = testISO; 140 | component.currentActivity.iso22 = testISO22; 141 | component.currentActivity.uuid = uuid; 142 | 143 | fixture.detectChanges(); 144 | const HTMLElement: HTMLElement = fixture.nativeElement; 145 | const contentDisplayedinParagraphTag = 146 | HTMLElement.querySelectorAll('#references')!; 147 | 148 | expect(contentDisplayedinParagraphTag[0].textContent).toContain( 149 | component.SAMMVersion + 150 | testSAMM[0] + 151 | component.ISOVersion + 152 | testISO[0] + 153 | component.ISO22Version + 154 | testISO22[0] + 155 | component.openCREVersion + 156 | uuid 157 | ); 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /src/app/component/circular-heatmap/circular-heatmap.component.css: -------------------------------------------------------------------------------- 1 | .margin30 { 2 | margin-bottom: 30px; 3 | } 4 | .axis path, 5 | .axis line { 6 | fill: none; 7 | stroke: #000; 8 | shape-rendering: crispEdges; 9 | } 10 | 11 | .title-button { 12 | background-color: transparent; 13 | border: none; 14 | text-align: left; 15 | cursor: pointer; 16 | font-weight: 700; 17 | } 18 | 19 | .right-panel { 20 | margin: 5%; 21 | padding: 20px; 22 | height: 80vh; 23 | display: grid; 24 | grid-template-rows: auto 1fr auto; 25 | } 26 | 27 | .heatmapClass { 28 | display: grid; 29 | grid-template-rows: auto 1fr auto; 30 | grid-template-columns: 6fr 4fr; 31 | height: 100%; 32 | width: 100%; 33 | } 34 | 35 | .heatmapChart { 36 | grid-row: 1/4; 37 | display: grid; 38 | justify-items: center; 39 | align-content: space-between; 40 | } 41 | #chart { 42 | width: 100%; 43 | max-width: min(100vh - 60px, 100vw - 60px); 44 | } 45 | 46 | .downloadButtonClass { 47 | margin: 10px 0; 48 | } 49 | .overlay-details { 50 | z-index: 2; 51 | background-color: rgba(0, 0, 0, 0.555); 52 | backdrop-filter: blur(3px); 53 | position: absolute; 54 | /* padding: 6em; */ 55 | /* margin-left: 20%; */ 56 | width: 60%; 57 | min-height: 100%; 58 | } 59 | .overlay-modal { 60 | /* border: 1px solid black; */ 61 | margin: 5em; 62 | background-color: rgb(238, 238, 238); 63 | padding: 1em; 64 | border-radius: 1em; 65 | height: 100%; 66 | } 67 | 68 | .overlay-header { 69 | display: grid; 70 | grid-template-columns: 1fr 0.1fr; 71 | } 72 | 73 | .overlay-close { 74 | border: none; 75 | background-color: rgba(0, 0, 0, 0); 76 | grid-column: 2/3; 77 | grid-row: 1/4; 78 | display: grid; 79 | justify-content: center; 80 | align-items: start; 81 | margin-left: auto; 82 | } 83 | 84 | /* overlay-close - light theme */ 85 | :host-context(body.light-theme) .overlay-close { 86 | color: black; 87 | } 88 | 89 | /* overlay-close - dark theme */ 90 | :host-context(body.dark-theme) .overlay-close { 91 | color: white; 92 | } 93 | 94 | .team-filter { 95 | padding: 0.4rem; 96 | grid-column: 2/3; 97 | display: flex; 98 | flex-direction: column; 99 | } 100 | 101 | .team-filter.hidden { 102 | height: 0; 103 | visibility: collapse; 104 | } 105 | 106 | .filter-toggle .hidden { 107 | display: none; 108 | } 109 | .team-list { 110 | list-style-type: none; 111 | margin: 0; 112 | padding: 0 1em; 113 | } 114 | .mat-chip-list { 115 | display: flex; 116 | flex-direction: row; 117 | flex-wrap: wrap; 118 | padding: 1rem; 119 | } 120 | .filter-container { 121 | position: relative; 122 | } 123 | button.filter-toggle { 124 | position: absolute; 125 | top: 2px; 126 | right: 2px; 127 | border: 0; 128 | background-color: transparent; 129 | z-index: 1; 130 | padding: 0; 131 | min-width: 0; 132 | line-height: 24px; 133 | } 134 | 135 | .footer-buttons { 136 | grid-column: 2/3; 137 | place-self: flex-end; 138 | margin: 0 1rem; 139 | padding-top: 1rem; 140 | display: flex; 141 | align-items: flex-end; 142 | flex-direction: column; 143 | } 144 | 145 | @media only screen and (max-width: 750px) { 146 | .heatmapClass { 147 | grid-template-rows: auto auto 1fr auto; 148 | grid-template-columns: 1fr; 149 | } 150 | 151 | .team-filter, .heatmapChart, .footer-buttons { 152 | grid-column: 1; 153 | } 154 | 155 | .team-filter { 156 | grid-row: 1; 157 | padding: 0.4rem; 158 | } 159 | 160 | .mat-chip-list { 161 | padding: 0.4rem; 162 | } 163 | 164 | .heatmapChart { 165 | grid-row: 2; 166 | } 167 | 168 | #chart { 169 | max-width: max(60vh, 60vw); 170 | } 171 | 172 | .overlay-details { 173 | width: 100%; 174 | } 175 | 176 | } -------------------------------------------------------------------------------- /src/app/component/circular-heatmap/circular-heatmap.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHandler } from '@angular/common/http'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 4 | import { CircularHeatmapComponent } from './circular-heatmap.component'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { MatChip } from '@angular/material/chips'; 7 | import { ModalMessageComponent } from '../modal-message/modal-message.component'; 8 | 9 | describe('CircularHeatmapComponent', () => { 10 | let component: CircularHeatmapComponent; 11 | let fixture: ComponentFixture; 12 | 13 | beforeEach(async () => { 14 | await TestBed.configureTestingModule({ 15 | declarations: [CircularHeatmapComponent, MatChip], 16 | imports: [RouterTestingModule], 17 | providers: [ 18 | ymlService, 19 | HttpClient, 20 | HttpHandler, 21 | { provide: ModalMessageComponent, useValue: {} }, 22 | ], 23 | }).compileComponents(); 24 | 25 | fixture = TestBed.createComponent(CircularHeatmapComponent); // Create fixture and component here 26 | component = fixture.componentInstance; 27 | fixture.detectChanges(); 28 | }); 29 | 30 | it('should create', () => { 31 | expect(component).toBeTruthy(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/app/component/dependency-graph/dependency-graph.component.css: -------------------------------------------------------------------------------- 1 | circle { 2 | cursor: pointer; 3 | stroke: #000; 4 | stroke-width: .5px; 5 | } 6 | line.link { 7 | fill: none; 8 | stroke: #000; 9 | stroke-width: 1.5px; 10 | } -------------------------------------------------------------------------------- /src/app/component/dependency-graph/dependency-graph.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/component/dependency-graph/dependency-graph.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHandler } from '@angular/common/http'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { ymlService } from '../../service/yaml-parser/yaml-parser.service'; 4 | import { DependencyGraphComponent } from './dependency-graph.component'; 5 | 6 | describe('DependencyGraphComponent', () => { 7 | let component: DependencyGraphComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | providers: [HttpClient, ymlService, HttpHandler], 13 | declarations: [DependencyGraphComponent], 14 | }).compileComponents(); 15 | }); 16 | 17 | beforeEach(() => { 18 | fixture = TestBed.createComponent(DependencyGraphComponent); 19 | component = fixture.componentInstance; 20 | fixture.detectChanges(); 21 | }); 22 | 23 | it('should create', () => { 24 | expect(component).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/app/component/dependency-graph/dependency-graph.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, ElementRef } from '@angular/core'; 2 | import * as d3 from 'd3'; 3 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 4 | import { Subscription } from 'rxjs'; 5 | import { ThemeService } from '../../service/theme.service'; 6 | 7 | export interface graphNodes { 8 | id: string; 9 | } 10 | 11 | export interface graphLinks { 12 | source: string; 13 | target: string; 14 | } 15 | 16 | export interface graph { 17 | nodes: graphNodes[]; 18 | links: graphLinks[]; 19 | } 20 | 21 | @Component({ 22 | selector: 'app-dependency-graph', 23 | templateUrl: './dependency-graph.component.html', 24 | styleUrls: ['./dependency-graph.component.css'], 25 | }) 26 | export class DependencyGraphComponent implements OnInit { 27 | SIZE_OF_NODE: number = 10; 28 | COLOR_OF_LINK: string = 'black'; 29 | COLOR_OF_NODE: string = '#55bc55'; 30 | BORDER_COLOR_OF_NODE: string = 'black'; 31 | simulation: any; 32 | YamlObject: any; 33 | graphData: graph = { nodes: [], links: [] }; 34 | visited: string[] = []; 35 | 36 | @Input() dimension: string = ''; 37 | @Input() subDimension: string = ''; 38 | @Input() activityName: string = ''; 39 | 40 | private themeSub: Subscription | undefined; 41 | currentTheme: string = 'light'; // default 42 | 43 | constructor( 44 | private yaml: ymlService, 45 | private elementRef: ElementRef, 46 | private themeService: ThemeService 47 | ) {} 48 | 49 | ngOnInit(): void { 50 | this.yaml.setURI('./assets/YAML/generated/generated.yaml'); 51 | 52 | this.currentTheme = this.themeService.getTheme(); 53 | this.themeSub = this.themeService.theme$.subscribe(theme => { 54 | this.currentTheme = theme; 55 | this.applyTextColor(theme); 56 | }); 57 | 58 | // Function sets data 59 | this.yaml.getJson().subscribe(data => { 60 | this.graphData = { nodes: [], links: [] }; 61 | this.YamlObject = data[this.dimension][this.subDimension]; 62 | this.populateGraphWithActivitiesCurrentActivityDependsOn( 63 | this.activityName 64 | ); 65 | this.populateGraphWithActivitiesThatDependsOnCurrentActivity( 66 | this.activityName 67 | ); 68 | //console.log({...this.graphData['nodes']}) 69 | 70 | console.log({ ...this.graphData }); 71 | this.generateGraph(this.activityName); 72 | }); 73 | } 74 | 75 | populateGraphWithActivitiesCurrentActivityDependsOn(activity: string): void { 76 | this.checkIfNodeHasBeenGenerated(activity); 77 | try { 78 | var activitysThatCurrenActivityIsDependentOn = 79 | this.YamlObject[activity]['dependsOn']; 80 | if (activitysThatCurrenActivityIsDependentOn) { 81 | for ( 82 | var j = 0; 83 | j < activitysThatCurrenActivityIsDependentOn.length; 84 | j++ 85 | ) { 86 | this.checkIfNodeHasBeenGenerated( 87 | activitysThatCurrenActivityIsDependentOn[j] 88 | ); 89 | this.graphData['links'].push({ 90 | source: activitysThatCurrenActivityIsDependentOn[j], 91 | target: activity, 92 | }); 93 | this.populateGraphWithActivitiesCurrentActivityDependsOn( 94 | activitysThatCurrenActivityIsDependentOn[j] 95 | ); 96 | } 97 | } 98 | } catch (e) { 99 | console.log(e); 100 | } 101 | //console.log({...this.graphData['nodes']}) 102 | } 103 | populateGraphWithActivitiesThatDependsOnCurrentActivity(activity: string) { 104 | var allActivitys = Object.keys(this.YamlObject); 105 | for (var i = 0; i < allActivitys.length; i++) { 106 | try { 107 | if (this.YamlObject[allActivitys[i]]['dependsOn'].includes(activity)) { 108 | this.checkIfNodeHasBeenGenerated(allActivitys[i]); 109 | this.graphData['links'].push({ 110 | source: activity, 111 | target: allActivitys[i], 112 | }); 113 | } 114 | } catch { 115 | continue; 116 | } 117 | } 118 | } 119 | 120 | checkIfNodeHasBeenGenerated(activity: string) { 121 | if (!this.visited.includes(activity)) { 122 | this.graphData['nodes'].push({ id: activity }); 123 | this.visited.push(activity); 124 | } 125 | } 126 | 127 | applyTextColor(theme: string): void { 128 | const fill = theme === 'dark' ? '#ffffff' : '#000000'; 129 | const selectedNodeColor = theme === 'dark' ? '#666666' : 'yellow'; 130 | const defaultNodeColor = this.COLOR_OF_NODE; 131 | 132 | d3.select(this.elementRef.nativeElement) 133 | .selectAll('text') 134 | .attr('fill', fill); 135 | 136 | d3.select(this.elementRef.nativeElement) 137 | .selectAll('circle') 138 | .attr('fill', (d: any) => 139 | d.id === this.activityName ? selectedNodeColor : defaultNodeColor 140 | ); 141 | } 142 | 143 | generateGraph(activity: string): void { 144 | const selectedNodeColor = 145 | this.currentTheme === 'dark' ? '#666666' : 'yellow'; 146 | 147 | let svg = d3.select('svg'), 148 | width = +svg.attr('width'), 149 | height = +svg.attr('height'); 150 | 151 | this.simulation = d3 152 | .forceSimulation() 153 | .force( 154 | 'link', 155 | d3.forceLink().id(function (d: any) { 156 | return d.id; 157 | }) 158 | ) 159 | .force('charge', d3.forceManyBody().strength(-12000)) 160 | .force('center', d3.forceCenter(width / 2, height / 2)); 161 | 162 | svg 163 | .append('defs') 164 | .append('marker') 165 | .attr('id', 'arrowhead') 166 | .attr('viewBox', '-0 -5 10 10') 167 | .attr('refX', 18) 168 | .attr('refY', 0) 169 | .attr('orient', 'auto') 170 | .attr('markerWidth', 13) 171 | .attr('markerHeight', 13) 172 | .attr('xoverflow', 'visible') 173 | .append('svg:path') 174 | .attr('d', 'M 0,-5 L 10 ,0 L 0,5') 175 | .attr('fill', this.COLOR_OF_LINK) 176 | .style('stroke', 'none'); 177 | 178 | let link = svg 179 | .append('g') 180 | .attr('class', 'links') 181 | .selectAll('line') 182 | .data(this.graphData['links']) 183 | .enter() 184 | .append('line') 185 | .style('stroke', this.COLOR_OF_LINK) 186 | .attr('marker-end', 'url(#arrowhead)'); 187 | 188 | let node = svg 189 | .append('g') 190 | .attr('class', 'nodes') 191 | .selectAll('g') 192 | .data(this.graphData['nodes']) 193 | .enter() 194 | .append('g'); 195 | 196 | var defaultNodeColor = this.COLOR_OF_NODE; 197 | node 198 | .append('circle') 199 | .attr('r', 10) 200 | .attr('fill', (d: any) => 201 | d.id === this.activityName ? selectedNodeColor : defaultNodeColor 202 | ); 203 | 204 | node 205 | .append('text') 206 | .attr('dy', '.35em') 207 | .attr('text-anchor', 'middle') 208 | .text(function (d) { 209 | return d.id; 210 | }); 211 | 212 | this.applyTextColor(this.currentTheme); 213 | 214 | this.simulation.nodes(this.graphData['nodes']).on('tick', ticked); 215 | 216 | this.simulation.force('link').links(this.graphData['links']); 217 | 218 | function ticked() { 219 | link 220 | .attr('x1', function (d: any) { 221 | return d.source.x; 222 | }) 223 | .attr('y1', function (d: any) { 224 | return d.source.y; 225 | }) 226 | .attr('x2', function (d: any) { 227 | return d.target.x; 228 | }) 229 | .attr('y2', function (d: any) { 230 | return d.target.y; 231 | }); 232 | 233 | node.attr('transform', function (d: any) { 234 | return 'translate(' + d.x + ',' + d.y + ')'; 235 | }); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/app/component/logo/logo.component.css: -------------------------------------------------------------------------------- 1 | .logo { 2 | width: 200px; 3 | height: auto; 4 | text-align: center; 5 | padding: 5px; 6 | /*border: 3px solid blue; */ 7 | } -------------------------------------------------------------------------------- /src/app/component/logo/logo.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/app/component/logo/logo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LogoComponent } from './logo.component'; 4 | 5 | describe('LogoComponent', () => { 6 | let component: LogoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [LogoComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(LogoComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/component/logo/logo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-logo', 5 | templateUrl: './logo.component.html', 6 | styleUrls: ['./logo.component.css'], 7 | }) 8 | export class LogoComponent { 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/component/mapping/mapping.component.css: -------------------------------------------------------------------------------- 1 | .content{ 2 | width: 100%; 3 | /*background-color: gainsboro;*/ 4 | } 5 | 6 | .mat-form-field{ 7 | margin: 20px; 8 | width: 50%; 9 | /*background-color: coral;*/ 10 | } 11 | 12 | .matrix-table{ 13 | margin: 20px; 14 | } 15 | 16 | .mat-cell{ 17 | padding: 20px; 18 | width: 12.5%; 19 | max-width: 12.5%; 20 | word-wrap: break-word; 21 | } 22 | 23 | .mat-header-cell{ 24 | padding: 10px; 25 | width: 12.5%; 26 | max-width: 12.5%; 27 | word-wrap: break-word; 28 | font-size: 16px; 29 | font-weight: bold; 30 | } 31 | 32 | .sort-section{ 33 | margin: 30px; 34 | margin-right: 60px; 35 | float: right; 36 | } 37 | 38 | .filterandsort{ 39 | /*background-color: brown;*/ 40 | } 41 | 42 | .hide{ 43 | display: none; 44 | } 45 | 46 | .export-button{ 47 | margin: 30px; 48 | margin-top: 35px; 49 | float: right; 50 | } 51 | 52 | .right-section{ 53 | /*background-color: mediumorchid;*/ 54 | float: right; 55 | } -------------------------------------------------------------------------------- /src/app/component/mapping/mapping.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | Filter 6 | 7 | 8 | {{ chip }} 9 | 12 | 13 | 19 | 20 | 23 | 26 | {{ filter }} 27 | 28 | 29 | 30 | 31 |
32 |
33 | 36 |
37 |
38 | 39 | Activity 44 | SAMM 49 | ISO 27001:2017 54 | ISO 27001:2022 59 | 60 |
61 |
62 |
63 | 64 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 95 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 116 | 117 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 138 | 139 | 146 | 147 | 148 | 149 | 150 | 151 |
Dimension{{ element.dimension }}Sub-Dimension{{ element.subDimension }}Activity{{ element.activityName }}SAMM{{ element.samm2 }}SAMM 96 |
    97 |
  • 98 | {{ sammElement }} 99 |
  • 100 |
101 |
ISO 27001:2017{{ element.ISO17 }}ISO 27001:2017 118 |
    119 |
  • 120 | {{ ISOElement }} 121 |
  • 122 |
123 |
ISO 27001:2022{{ element.ISO22 }}ISO 27001:2022 140 |
    141 |
  • 142 | {{ ISO22Element }} 143 |
  • 144 |
145 |
152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 176 | 179 | 180 | 181 | 182 | 187 | 192 | 197 | 202 | 207 | 212 | 217 | 222 | 227 | 232 | 237 | 243 | 248 | 253 | 258 | 263 | 268 | 273 | 278 | 283 | 284 |
DimensionSub DimensionActivityLevelDescriptionRiskMeasureKnowledgeResourcesTimeUsefulnessImplementationAssessmentCommentsDepends OnSAMMISO 27001:2017ISO 27001:2022 174 | {{ team + '- Implemented' }} 175 | 177 | {{ team + '- Evidence' }} 178 |
183 | 184 | {{ item.dimension | slice : 0 : 32767 }} 185 | 186 | 188 | 189 | {{ item.subDimension | slice : 0 : 32767 }} 190 | 191 | 193 | 194 | {{ item.activityName | slice : 0 : 32767 }} 195 | 196 | 198 | 199 | {{ '' + item.level | slice : 0 : 32767 }} 200 | 201 | 203 | 204 | {{ item.description | slice : 0 : 32767 }} 205 | 206 | 208 | 209 | {{ item.risk | slice : 0 : 32767 }} 210 | 211 | 213 | 214 | {{ item.measure | slice : 0 : 32767 }} 215 | 216 | 218 | 219 | {{ item.knowledge | slice : 0 : 32767 }} 220 | 221 | 223 | 224 | {{ item.resources | slice : 0 : 32767 }} 225 | 226 | 228 | 229 | {{ item.time | slice : 0 : 32767 }} 230 | 231 | 233 | 234 | {{ item.usefulness | slice : 0 : 32767 }} 235 | 236 | 238 | 240 | {{ item.implementation | slice : 0 : 32767 }} 241 | 242 | 244 | 245 | {{ item.assessment | slice : 0 : 32767 }} 246 | 247 | 249 | 250 | {{ item.comments | slice : 0 : 32767 }} 251 | 252 | 254 | 255 | {{ item.dependsOn | slice : 0 : 32767 }} 256 | 257 | 259 | 260 | {{ item.samm2 | slice : 0 : 32767 }} 261 | 262 | 264 | 265 | {{ item.ISO17 | slice : 0 : 32767 }} 266 | 267 | 269 | 270 | {{ item.ISO22 | slice : 0 : 32767 }} 271 | 272 | 274 | 275 | {{ item.teamImplementation[key] }} 276 | 277 | 279 | 280 | {{ item.teamsEvidence[key] }} 281 | 282 |
285 |
286 | -------------------------------------------------------------------------------- /src/app/component/mapping/mapping.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHandler } from '@angular/common/http'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { MatAutocomplete } from '@angular/material/autocomplete'; 4 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 5 | 6 | import { MappingComponent } from './mapping.component'; 7 | 8 | describe('MappingComponent', () => { 9 | let component: MappingComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async () => { 13 | await TestBed.configureTestingModule({ 14 | providers: [ymlService, HttpClient, HttpHandler], 15 | declarations: [MappingComponent, MatAutocomplete], 16 | }).compileComponents(); 17 | }); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(MappingComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | 29 | it('check for table generation', () => { 30 | const HTMLElement: HTMLElement = fixture.nativeElement; 31 | const table = HTMLElement.querySelector('table')!; 32 | expect(table).toBeTruthy(); 33 | }); 34 | 35 | it('check for chip deletion', () => { 36 | component.currentChip = ['row1', 'row2']; 37 | component.removeChip('row1'); 38 | const newChipRow = ['row2']; 39 | fixture.detectChanges(); 40 | expect(component.currentChip).toEqual(newChipRow); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/app/component/matrix/matrix.component.css: -------------------------------------------------------------------------------- 1 | .content { 2 | width: 100%; 3 | /*background-color: gainsboro;*/ 4 | } 5 | .mat-form-field { 6 | margin: 20px; 7 | margin-bottom: 0; 8 | width: 90%; 9 | } 10 | 11 | .matrix-table { 12 | margin: 20px; 13 | } 14 | 15 | .mat-cell { 16 | padding: 20px; 17 | /* padding-bottom: 0; */ 18 | width: 12.5%; 19 | max-width: 17%; 20 | word-wrap: break-word; 21 | } 22 | 23 | .mat-header-cell { 24 | padding: 10px; 25 | width: 12.5%; 26 | max-width: 17%; 27 | word-wrap: break-word; 28 | font-size: 16px; 29 | font-weight: bold; 30 | } 31 | 32 | .mat-cell ul.activity-list { 33 | list-style-type: disclosure-closed; 34 | } 35 | 36 | .mat-cell-activity { 37 | margin-bottom: 1em; 38 | position: relative; 39 | } 40 | .mat-cell-activity a.activity-title { 41 | text-decoration: unset; 42 | margin: 0; 43 | } 44 | .mat-cell-activity a::after { 45 | content: ""; 46 | position: absolute; 47 | top: 0; 48 | left: 0; 49 | width: 100%; 50 | height: 100%; 51 | } 52 | .mat-cell-activity a.activity-title:link { 53 | color: inherit; 54 | } 55 | 56 | .table-small-width { 57 | width: 5%; 58 | max-width: 9%; 59 | } 60 | 61 | .tags-activity { 62 | font-weight: 800; 63 | font-style: italic; 64 | font-size: 12px; 65 | } 66 | /*tag activity - light */ 67 | :host-context(body.light-theme) .tags-activity, 68 | :host-context(body.light-theme) .tags-activity span { 69 | color: rgb(0, 113, 151); 70 | } 71 | 72 | /*tag activity - dark */ 73 | :host-context(body.dark-theme) .tags-activity, 74 | :host-context(body.dark-theme) .tags-activity span { 75 | color: #397af4; 76 | } 77 | 78 | .reset-button { 79 | background-color: #66bb6a; 80 | display: block; 81 | margin: 0 20px; 82 | padding: 7px 12px; 83 | border-radius: 16px; 84 | font: 500 14px / 20px Roboto, 'Helvetica Neue', sans-serif; 85 | } 86 | -------------------------------------------------------------------------------- /src/app/component/matrix/matrix.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | SubDimension Filter 5 | 6 | 12 | {{ subDimension }} 13 | 14 | 15 | 16 | 17 | Activity Tag Filter 18 | 19 | 25 | {{ tag }} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | 41 | 48 | 49 | 50 | 51 | 52 | 55 | 58 | 59 | 60 |
61 | 62 |
63 | 112 | 113 | 114 | 115 | 116 |
39 | Dimension 40 | 42 | 46 | {{ element.Dimension }} 47 | 53 | Sub-Dimension 54 | 56 | {{ element.SubDimension }} 57 | {{ levels[i] }} 64 |
    65 |
  • 68 |
    69 |
    70 | {{ activity }} 85 |
    86 | 87 | [ 88 | 95 | {{ tag }} 96 | 103 | , 104 | 105 | 106 | ] 107 | 108 |
    109 |
  • 110 |
111 |
117 |
118 | -------------------------------------------------------------------------------- /src/app/component/matrix/matrix.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule, HttpHandler } from '@angular/common/http'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 4 | // import { MatAutocomplete } from '@angular/material/autocomplete'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 7 | import { MatrixComponent } from './matrix.component'; 8 | import { MatChip } from '@angular/material/chips'; 9 | 10 | describe('MatrixComponent', () => { 11 | let component: MatrixComponent; 12 | let fixture: ComponentFixture; 13 | 14 | beforeEach(async () => { 15 | await TestBed.configureTestingModule({ 16 | providers: [ymlService, HttpClientTestingModule], 17 | imports: [RouterTestingModule, HttpClientModule], 18 | declarations: [MatrixComponent, MatChip], 19 | }).compileComponents(); 20 | }); 21 | 22 | beforeEach(() => { 23 | fixture = TestBed.createComponent(MatrixComponent); 24 | component = fixture.componentInstance; 25 | fixture.detectChanges(); 26 | }); 27 | 28 | it('should create', () => { 29 | fixture.detectChanges(); 30 | expect(component).toBeTruthy(); 31 | }); 32 | 33 | it('check for table generation', () => { 34 | const HTMLElement: HTMLElement = fixture.nativeElement; 35 | const table = HTMLElement.querySelector('table')!; 36 | expect(table).toBeTruthy(); 37 | }); 38 | 39 | it('check for chip deletion', () => { 40 | component.listSubDimension = ['row1', 'row2']; 41 | component.currentSubDimensions = ['row2']; 42 | component.listTags = ['row1', 'row2']; 43 | component.currentTags = ['row2']; 44 | const newChipRow = ['row2']; 45 | fixture.detectChanges(); 46 | expect(component.currentSubDimensions).toEqual(newChipRow); 47 | expect(component.currentTags).toEqual(newChipRow); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /src/app/component/modal-message/modal-message.component.css: -------------------------------------------------------------------------------- 1 | .dialog { 2 | margin: 0.5em; 3 | padding: 1em; 4 | } 5 | 6 | .dialog-buttons { 7 | display: flex; 8 | justify-content: flex-end; 9 | } 10 | 11 | button { 12 | min-width: 5rem; 13 | margin: 0 1rem; 14 | } -------------------------------------------------------------------------------- /src/app/component/modal-message/modal-message.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ data.title }}

3 |

4 |
5 |
6 | 14 |
15 | -------------------------------------------------------------------------------- /src/app/component/modal-message/modal-message.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { DialogInfo, ModalMessageComponent } from './modal-message.component'; 3 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; 4 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { MatDialogModule } from '@angular/material/dialog'; 6 | 7 | describe('ModalMessageComponent', () => { 8 | let component: ModalMessageComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | imports: [NoopAnimationsModule, MatDialogModule], 14 | declarations: [ModalMessageComponent], 15 | providers: [ 16 | { provide: MatDialogRef, useValue: {} }, 17 | { provide: MAT_DIALOG_DATA, useValue: {} }, 18 | ], 19 | }).compileComponents(); 20 | }); 21 | 22 | beforeEach(() => { 23 | fixture = TestBed.createComponent(ModalMessageComponent); 24 | component = fixture.componentInstance; 25 | fixture.detectChanges(); 26 | }); 27 | 28 | it('should create', () => { 29 | expect(component).toBeTruthy(); 30 | }); 31 | 32 | it('should render markdown correctly in the dialog', () => { 33 | const dialogInfo: DialogInfo = new DialogInfo('A **test** markdown.'); 34 | const dialogRef: MatDialogRef = 35 | component.openDialog(dialogInfo); 36 | 37 | expect(dialogRef.componentInstance.data.message).toContain( 38 | 'test' 39 | ); 40 | }); 41 | 42 | it('should render markdown correctly in the dialog', () => { 43 | const dialogInfo: DialogInfo = new DialogInfo('A **test** markdown.'); 44 | const dialogRef: MatDialogRef = 45 | component.openDialog(dialogInfo); 46 | 47 | // Check if markdown rendering is applied 48 | expect(dialogRef.componentInstance.data.message).toContain( 49 | 'test' 50 | ); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /src/app/component/modal-message/modal-message.component.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Component, OnInit } from '@angular/core'; 2 | import { 3 | MAT_DIALOG_DATA, 4 | MatDialogRef, 5 | MatDialog, 6 | MatDialogConfig, 7 | } from '@angular/material/dialog'; 8 | import * as md from 'markdown-it'; 9 | 10 | @Component({ 11 | selector: 'app-modal-message', 12 | templateUrl: './modal-message.component.html', 13 | styleUrls: ['./modal-message.component.css'], 14 | }) 15 | export class ModalMessageComponent implements OnInit { 16 | data: DialogInfo; 17 | markdown: md = md(); 18 | 19 | DSOMM_host: string = 'https://github.com/devsecopsmaturitymodel'; 20 | DSOMM_url: string = `${this.DSOMM_host}/DevSecOps-MaturityModel-data`; 21 | meassageTemplates: Record = { 22 | generated_yaml: new DialogInfo( 23 | `{message}\n\n` + 24 | `Please download the activity template \`generated.yaml\` ` + 25 | `from [DSOMM-data](${this.DSOMM_url}) on GitHub.\n\n` + 26 | 'The DSOMM activities are maintained and distributed ' + 27 | 'separately from the software.', 28 | 'DSOMM startup problems' 29 | ), 30 | }; 31 | 32 | constructor( 33 | public dialog: MatDialog, 34 | public dialogRef: MatDialogRef, 35 | @Inject(MAT_DIALOG_DATA) data: DialogInfo 36 | ) { 37 | this.data = data; 38 | } 39 | 40 | // eslint-disable-next-line @angular-eslint/no-empty-lifecycle-method 41 | ngOnInit(): void {} 42 | 43 | openDialog( 44 | dialogInfo: DialogInfo | string 45 | ): MatDialogRef { 46 | if (typeof dialogInfo === 'string') { 47 | dialogInfo = new DialogInfo(dialogInfo); 48 | } 49 | if ( 50 | dialogInfo.template && 51 | this.meassageTemplates.hasOwnProperty(dialogInfo.template) 52 | ) { 53 | let template: DialogInfo = this.meassageTemplates[dialogInfo.template]; 54 | dialogInfo.title = dialogInfo.title || template?.title; 55 | dialogInfo.message = template?.message?.replace( 56 | '{message}', 57 | dialogInfo.message 58 | ); 59 | } 60 | 61 | dialogInfo.message = this.markdown.render(dialogInfo.message); 62 | 63 | const dialogConfig = new MatDialogConfig(); 64 | dialogConfig.id = 'modal-message'; 65 | dialogConfig.disableClose = true; 66 | dialogConfig.data = dialogInfo; 67 | dialogConfig.autoFocus = false; 68 | this.dialogRef = this.dialog.open(ModalMessageComponent, dialogConfig); 69 | return this.dialogRef; 70 | } 71 | 72 | closeDialog(buttonName: string) { 73 | this.dialogRef?.close(buttonName); 74 | } 75 | } 76 | 77 | export class DialogInfo { 78 | title: string = ''; 79 | template: string | null = ''; 80 | message: string = ''; 81 | buttons: string[] = ['OK']; 82 | 83 | constructor(msg: string = '', title: string = '') { 84 | this.message = msg; 85 | this.title = title; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/app/component/readme-to-html/readme-to-html.component.css: -------------------------------------------------------------------------------- 1 | .main-section{ 2 | /*background-color: aqua;*/ 3 | padding: 30px; 4 | padding-top: 0px; 5 | max-width: 40rem; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/component/readme-to-html/readme-to-html.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | -------------------------------------------------------------------------------- /src/app/component/readme-to-html/readme-to-html.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | import { ReadmeToHtmlComponent } from './readme-to-html.component'; 4 | 5 | describe('ReadmeToHtmlComponent', () => { 6 | let component: ReadmeToHtmlComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [HttpClientTestingModule], 12 | declarations: [ReadmeToHtmlComponent], 13 | }).compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ReadmeToHtmlComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/component/readme-to-html/readme-to-html.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import * as md from 'markdown-it'; 3 | import { HttpClient } from '@angular/common/http'; 4 | 5 | @Component({ 6 | selector: 'app-readme-to-html', 7 | templateUrl: './readme-to-html.component.html', 8 | styleUrls: ['./readme-to-html.component.css'], 9 | }) 10 | export class ReadmeToHtmlComponent implements OnInit { 11 | @Input() MDFile: string = ''; 12 | markdown: md = md({ 13 | html: true, 14 | }); 15 | markdownURI: any; 16 | toRender: string = ''; 17 | constructor(private http: HttpClient) {} 18 | 19 | ngOnInit(): void { 20 | this.loadMarkdownFiles(this.MDFile); 21 | } 22 | 23 | async loadMarkdownFiles(MDFile: string): Promise { 24 | try { 25 | this.markdownURI = await this.http 26 | .get(MDFile, { responseType: 'text' }) 27 | .toPromise(); 28 | this.toRender = this.markdown.render(this.markdownURI); 29 | return true; 30 | } catch { 31 | this.toRender = 'Markdown file could not be found'; 32 | return false; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/component/sidenav-buttons/sidenav-buttons.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/app/component/sidenav-buttons/sidenav-buttons.component.css -------------------------------------------------------------------------------- /src/app/component/sidenav-buttons/sidenav-buttons.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | {{ Icons[i] }} 8 |

{{ Options[i] }}

9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{ isNightMode ? 'light_mode' : 'dark_mode' }} 19 | 20 |

21 | {{ isNightMode ? 'Switch to Light Mode' : 'Switch to Dark Mode' }} 22 |

23 |
24 |
25 | -------------------------------------------------------------------------------- /src/app/component/sidenav-buttons/sidenav-buttons.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { SidenavButtonsComponent } from './sidenav-buttons.component'; 5 | 6 | describe('SidenavButtonsComponent', () => { 7 | let component: SidenavButtonsComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async () => { 11 | await TestBed.configureTestingModule({ 12 | declarations: [SidenavButtonsComponent], 13 | }).compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SidenavButtonsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | 26 | it('check for navigation list generation', () => { 27 | const HTMLElement: HTMLElement = fixture.nativeElement; 28 | const NavigationList = HTMLElement.querySelector('mat-nav-list')!; 29 | //console.log(NavigationList); 30 | expect(NavigationList).toBeTruthy(); 31 | }); 32 | 33 | it('check for navigation names being shown in the same order as options array', () => { 34 | const HTMLElement: HTMLElement = fixture.nativeElement; 35 | const NavigationList = HTMLElement.querySelectorAll('a > h3')!; 36 | let NavigationNamesBeingShown = []; 37 | for (var x = 0; x < NavigationList.length; x += 1) { 38 | NavigationNamesBeingShown.push(NavigationList[x].textContent); 39 | } 40 | //console.log({ ...NavigationNamesBeingShown }); 41 | //console.log(component.Options); 42 | expect(NavigationNamesBeingShown).toEqual(component.Options); 43 | }); 44 | 45 | it('ensure all navigation options has its own icon and route', () => { 46 | expect(component.Icons.length).toEqual(component.Options.length); 47 | expect(component.Routing.length).toEqual(component.Options.length); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /src/app/component/sidenav-buttons/sidenav-buttons.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ThemeService } from '../../service/theme.service'; 3 | 4 | @Component({ 5 | selector: 'app-sidenav-buttons', 6 | templateUrl: './sidenav-buttons.component.html', 7 | styleUrls: ['./sidenav-buttons.component.css'], 8 | }) 9 | export class SidenavButtonsComponent implements OnInit { 10 | Options: string[] = [ 11 | 'Overview', 12 | 'Matrix', 13 | 'Mappings', 14 | 'Usage', 15 | 'Teams', 16 | 'About Us', 17 | 'DSOMM User Day 2024', 18 | ]; 19 | Icons: string[] = [ 20 | 'pie_chart', 21 | 'table_chart', 22 | 'timeline', 23 | 'description', 24 | 'people', 25 | 'info', 26 | 'school', 27 | ]; 28 | Routing: string[] = [ 29 | '/circular-heatmap', 30 | '/matrix', 31 | '/mapping', 32 | '/usage', 33 | '/teams', 34 | '/about', 35 | '/userday', 36 | ]; 37 | 38 | isNightMode = false; 39 | 40 | constructor(private themeService: ThemeService) {} 41 | 42 | ngOnInit(): void { 43 | const currentTheme = this.themeService.getTheme(); 44 | this.isNightMode = currentTheme === 'dark'; 45 | } 46 | 47 | toggleTheme(): void { 48 | this.isNightMode = !this.isNightMode; 49 | const newTheme = this.isNightMode ? 'dark' : 'light'; 50 | this.themeService.setTheme(newTheme); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/component/teams/teams.component.css: -------------------------------------------------------------------------------- 1 | h2 { 2 | color: #000000; 3 | text-align: center; 4 | font-weight: 800; 5 | font-size: 2em; 6 | margin: 1em 1em; 7 | } 8 | h3 { 9 | color: #000000; 10 | text-align: center; 11 | font-weight: 500; 12 | font-size: 1.2em; 13 | font-style: italic; 14 | margin: 1em 1em; 15 | } 16 | .team-list { 17 | width: 70%; 18 | overflow-x: auto; 19 | white-space: nowrap; 20 | padding: 20px; 21 | margin: 0 auto; 22 | } 23 | 24 | .team-list ul { 25 | list-style-type: none; 26 | padding: 0; 27 | margin: 0; 28 | display: flex; /* Use flex layout */ 29 | } 30 | 31 | .team-list ul li { 32 | flex: 0 0 150px; /* Set a fixed width */ 33 | height: 100px; 34 | background-color: #66bb6a; 35 | border-radius: 10px; 36 | color: white; 37 | margin-right: 20px; 38 | display: flex; /* Use flex layout */ 39 | justify-content: center; /* Center horizontally */ 40 | align-items: center; /* Center vertically */ 41 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 42 | font-size: 16px; 43 | font-weight: bold; 44 | } 45 | -------------------------------------------------------------------------------- /src/app/component/teams/teams.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Team List

3 |
4 |
    5 |
  • {{ team }}
  • 6 |
7 |
8 |

Team Group

9 |
10 |

{{ group.key }}

11 |
12 |
    13 |
  • {{ team }}
  • 14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/app/component/teams/teams.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientModule, HttpHandler } from '@angular/common/http'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 4 | // import { MatAutocomplete } from '@angular/material/autocomplete'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 7 | import { MatChip } from '@angular/material/chips'; 8 | 9 | import { TeamsComponent } from './teams.component'; 10 | 11 | describe('TeamsComponent', () => { 12 | let component: TeamsComponent; 13 | let fixture: ComponentFixture; 14 | 15 | beforeEach(async () => { 16 | await TestBed.configureTestingModule({ 17 | providers: [ymlService, HttpClientTestingModule], 18 | imports: [RouterTestingModule, HttpClientModule], 19 | declarations: [TeamsComponent, MatChip], 20 | }).compileComponents(); 21 | }); 22 | 23 | beforeEach(() => { 24 | fixture = TestBed.createComponent(TeamsComponent); 25 | component = fixture.componentInstance; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should create', () => { 30 | expect(component).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/app/component/teams/teams.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ymlService } from 'src/app/service/yaml-parser/yaml-parser.service'; 3 | import * as yaml from 'js-yaml'; 4 | 5 | @Component({ 6 | selector: 'app-teams', 7 | templateUrl: './teams.component.html', 8 | styleUrls: ['./teams.component.css'], 9 | }) 10 | export class TeamsComponent implements OnInit { 11 | YamlObject: any; 12 | teamList: any; 13 | teamGroups: Map = new Map(); 14 | 15 | constructor(private yaml: ymlService) {} 16 | 17 | ngOnInit(): void { 18 | this.yaml.setURI('./assets/YAML/meta.yaml'); 19 | // Function sets column header 20 | this.yaml.getJson().subscribe(data => { 21 | this.YamlObject = data; 22 | 23 | console.log(this.YamlObject); 24 | this.teamList = this.YamlObject['teams']; 25 | this.teamGroups = this.YamlObject['teamGroups']; 26 | 27 | console.log('teamList', this.teamList); 28 | console.log('teamGroups', this.teamGroups); 29 | }); 30 | } 31 | 32 | getTeamArray(key: string): string[] { 33 | return this.teamGroups.get(key) || []; 34 | } 35 | 36 | unsorted() { 37 | return 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/app/component/top-header/top-header.component.css: -------------------------------------------------------------------------------- 1 | .mat-display-1 { 2 | /*background-color: goldenrod;*/ 3 | padding: 20px; 4 | } -------------------------------------------------------------------------------- /src/app/component/top-header/top-header.component.html: -------------------------------------------------------------------------------- 1 |

{{ section }}

2 | -------------------------------------------------------------------------------- /src/app/component/top-header/top-header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TopHeaderComponent } from './top-header.component'; 4 | 5 | describe('TopHeaderComponent', () => { 6 | let component: TopHeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [TopHeaderComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(TopHeaderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | 25 | it('check if header is being generated', () => { 26 | const HTMLElement: HTMLElement = fixture.nativeElement; 27 | const heading = HTMLElement.querySelector('h1')!; 28 | expect(heading.textContent).toEqual('Default'); 29 | }); 30 | 31 | it('check if header is being changed', () => { 32 | const changedTextElement = 'changed'; 33 | component.section = changedTextElement; 34 | fixture.detectChanges(); 35 | const HTMLElement: HTMLElement = fixture.nativeElement; 36 | const heading = HTMLElement.querySelector('h1')!; 37 | expect(heading.textContent).toEqual('changed'); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/app/component/top-header/top-header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-top-header', 5 | templateUrl: './top-header.component.html', 6 | styleUrls: ['./top-header.component.css'], 7 | }) 8 | export class TopHeaderComponent { 9 | @Input() section: string = 'Default'; 10 | 11 | constructor() {} 12 | } 13 | -------------------------------------------------------------------------------- /src/app/component/usage/usage.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/app/component/usage/usage.component.css -------------------------------------------------------------------------------- /src/app/component/usage/usage.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/component/usage/usage.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UsageComponent } from './usage.component'; 4 | import { ActivatedRoute } from '@angular/router'; 5 | import { of } from 'rxjs'; 6 | 7 | describe('UsageComponent', () => { 8 | let component: UsageComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async () => { 12 | await TestBed.configureTestingModule({ 13 | declarations: [UsageComponent], 14 | }).compileComponents(); 15 | }); 16 | 17 | it('should create', () => { 18 | TestBed.overrideProvider(ActivatedRoute, { 19 | useValue: { params: of({}) }, 20 | }); 21 | 22 | fixture = TestBed.createComponent(UsageComponent); 23 | component = fixture.componentInstance; 24 | fixture.detectChanges(); 25 | 26 | expect(component).toBeTruthy(); 27 | expect(component.page).toBe('USAGE'); 28 | }); 29 | 30 | it('should load page', () => { 31 | TestBed.overrideProvider(ActivatedRoute, { 32 | useValue: { params: of({ page: 'test-page' }) }, 33 | }); 34 | 35 | fixture = TestBed.createComponent(UsageComponent); 36 | component = fixture.componentInstance; 37 | fixture.detectChanges(); 38 | 39 | expect(component.page).toBe('test-page'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/app/component/usage/usage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-usage', 6 | templateUrl: './usage.component.html', 7 | styleUrls: ['./usage.component.css'], 8 | }) 9 | export class UsageComponent implements OnInit { 10 | page: string = 'USAGE'; 11 | constructor(private route: ActivatedRoute) {} 12 | 13 | ngOnInit() { 14 | if (this.route && this.route.params) { 15 | this.route.params.subscribe(params => { 16 | let page = params['page']; 17 | // CWE-79 - sanitize input 18 | if (page.match(/^[\w.-]+$/)) { 19 | this.page = page; 20 | } 21 | }); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/component/userday/userday.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/app/component/userday/userday.component.css -------------------------------------------------------------------------------- /src/app/component/userday/userday.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 5 |
6 | -------------------------------------------------------------------------------- /src/app/component/userday/userday.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserdayComponent } from './userday.component'; 4 | 5 | describe('UserdayComponent', () => { 6 | let component: UserdayComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [UserdayComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UserdayComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/component/userday/userday.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-userday', 5 | templateUrl: './userday.component.html', 6 | styleUrls: ['./userday.component.css'], 7 | }) 8 | export class UserdayComponent { 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app/material/material.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { MatSidenavModule } from '@angular/material/sidenav'; 3 | import { MatButtonModule } from '@angular/material/button'; 4 | import { MatIconModule } from '@angular/material/icon'; 5 | import { MatListModule } from '@angular/material/list'; 6 | import { MatDividerModule } from '@angular/material/divider'; 7 | import { MatTableModule } from '@angular/material/table'; 8 | import { MatChipsModule } from '@angular/material/chips'; 9 | import { MatAutocompleteModule } from '@angular/material/autocomplete'; 10 | import { MatInputModule } from '@angular/material/input'; 11 | import { MatExpansionModule } from '@angular/material/expansion'; 12 | import { MatCardModule } from '@angular/material/card'; 13 | import { MatCheckboxModule } from '@angular/material/checkbox'; 14 | import { MatButtonToggleModule } from '@angular/material/button-toggle'; 15 | import { CommonModule } from '@angular/common'; 16 | 17 | const MaterialComponents = [ 18 | CommonModule, 19 | MatSidenavModule, 20 | MatButtonModule, 21 | MatIconModule, 22 | MatListModule, 23 | MatDividerModule, 24 | MatTableModule, 25 | MatChipsModule, 26 | MatAutocompleteModule, 27 | MatInputModule, 28 | MatExpansionModule, 29 | MatCardModule, 30 | MatCheckboxModule, 31 | MatButtonToggleModule, 32 | ]; 33 | 34 | @NgModule({ 35 | imports: [MaterialComponents], 36 | exports: [MaterialComponents], 37 | }) 38 | export class MaterialModule {} 39 | -------------------------------------------------------------------------------- /src/app/pipe/to-string-value.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { ToStringValuePipe } from './to-string-value.pipe'; 2 | 3 | describe('ToStringValuePipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new ToStringValuePipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app/pipe/to-string-value.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'ToStringValue', 5 | pure: true, 6 | }) 7 | export class ToStringValuePipe implements PipeTransform { 8 | transform(value: unknown): string { 9 | return value as string; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/service/theme.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class ThemeService { 6 | private themeSubject = new BehaviorSubject('light'); 7 | public readonly theme$ = this.themeSubject.asObservable(); 8 | 9 | constructor() {} 10 | 11 | initTheme(): void { 12 | const saved = localStorage.getItem('theme') || 'light'; 13 | this.setTheme(saved); 14 | } 15 | 16 | setTheme(theme: string): void { 17 | document.body.classList.remove('light-theme', 'dark-theme'); 18 | document.body.classList.add(`${theme}-theme`); 19 | localStorage.setItem('theme', theme); 20 | this.themeSubject.next(theme); 21 | } 22 | 23 | getTheme(): string { 24 | return this.themeSubject.value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/service/yaml-parser/yaml-parser.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHandler } from '@angular/common/http'; 2 | import { 3 | HttpClientTestingModule, 4 | HttpTestingController, 5 | } from '@angular/common/http/testing'; 6 | import { TestBed } from '@angular/core/testing'; 7 | import { ymlService } from './yaml-parser.service'; 8 | 9 | describe('YAMLParserService', () => { 10 | let service: ymlService; 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | providers: [HttpClientTestingModule, ymlService, HttpClient, HttpHandler], 15 | }); 16 | service = TestBed.inject(ymlService); 17 | }); 18 | 19 | it('should be created', () => { 20 | expect(service).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/service/yaml-parser/yaml-parser.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { map } from 'rxjs/operators'; 4 | import { Observable } from 'rxjs'; 5 | import { parse } from 'yamljs'; 6 | 7 | @Injectable() 8 | export class ymlService { 9 | private URI: string = './'; 10 | 11 | constructor(private http: HttpClient) {} 12 | 13 | setURI(URI_used: string) { 14 | this.URI = URI_used; 15 | } 16 | 17 | public getJson(): Observable { 18 | return this.http 19 | .get(this.URI, { 20 | observe: 'body', 21 | responseType: 'text', // This one here tells HttpClient to parse it as text, not as JSON 22 | }) 23 | .pipe( 24 | // Map Yaml to JavaScript Object 25 | map(yamlString => parse(yamlString)) 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/Markdown Files/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | From a startup to a multinational corporation the software development industry is currently dominated by agile frameworks and product teams and as part of it DevOps strategies. It has been observed that during the implementation, security aspects are usually neglected or are at least not sufficient taken account of. It is often the case that standard safety requirements of the production environment are not utilized or applied to the build pipeline in the continuous integration environment with containerization or concrete docker. Therefore, the docker registry is often not secured which might result in the theft of the entire company’s source code. 4 | 5 | The OWASP DevSecOps Maturity Model provides opportunities to harden DevOps strategies and shows how these can be prioritized. 6 | 7 | With the help of DevOps strategies security can also be enhanced. For example, each component such as application libraries and operating system libraries in docker images can be tested for known vulnerabilities. 8 | 9 | Attackers are intelligent and creative, equipped with new technologies and purpose. Under the guidance of the forward-looking DevSecOps Maturity Model, appropriate principles and measures are at hand implemented which counteract the attacks. 10 | 11 | # Usage 12 | 13 | Go to https://dsomm.owasp.org. 14 | 15 | * _matrix_ shows the dimensions, subdimensions and activities are described. 16 | * _Implementation Levels_ can be used to show the current implementation level by clicking on the specific activities which have been performed (it is recommended to use a gitops-like flow) 17 | * _Mappings_ Shows mappings to other standards and provides the ability to download an excel sheet 18 | * _Usage_ describes how to use DSOMM 19 | 20 | In this [video](https://www.youtube.com/watch?v=tX9RHZ_O5NU) Timo Pagel describes different strategic approaches for your secure DevOps strategy. The use OWASP DSOMM in combination with [OWASP SAMM](https//owaspsamm.org) is explained. 21 | 22 | In case you have evidence or review questions to gather evidence, you can add the attribute "evidence" to an activity which will be attached to an activity to provide it to your CISO or your customer's CISO. 23 | You can switch on to show open TODO's for evidence by changing IS_SHOW_EVIDENCE_TODO to true 'bib.php' `define(IS_SHOW_EVIDENCE_TODO, true);` 24 | 25 | This page uses the Browser's localStorage to store the state of the circular headmap. 26 | 27 | # Changes 28 | Changes to the application are displayed at the release page of [DevSecOps-MaturityModel](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/releases). 29 | 30 | Changes to the maturity model content are displayed at the release page of [DevSecOps-MaturityModel-data](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/releases). 31 | 32 | # Community 33 | Join #dsomm in [OWASP Slack](https://owasp.slack.com/join/shared_invite/zt-g398htpy-AZ40HOM1WUOZguJKbblqkw#/). 34 | Create issues or even better Pull Requests in [github](https://github.com/wurstbrot/DevSecOps-MaturityModel/). 35 | 36 | # Slides and talks 37 | * [Video: OWASP (DevSecOps) Projects, 2021-04-28, OWASP Stammtisch Frankfurt](https://www.youtube.com/watch?v=8webiYnF56A) 38 | * [Video: DSOMM Enhancement Workshop at Open Security Summit, 2021-04-16](https://youtu.be/H2BA6gaeKBE) 39 | * [Video: Strategic Usage of the OWASP Software Assurance Maturity Model and the OWASP DevSecOps Maturity Model, OWASP Jakarta](https://m.youtube.com/watch?v=lLMLGIzl56M) 40 | * [Slides: DSOMM Overview](https://docs.google.com/presentation/d/1eQcE_AsR1g6uOVf3B2Ehh1g0cHvPknkdLY4BzMYatSw/edit?usp=sharing) 41 | * [Video: GitHub practical DSOMM snippet on twitch](https://www.twitch.tv/githubenterprise/clip/EsteemedTriumphantMinkFailFish) 42 | * [Blog: GitHub on DSOMM](https://github.blog/2020-08-06-achieving-devsecops-maturity-with-a-developer-first-community-driven-approach/) 2020 43 | * [Video: Benutzung vom OWASP DevSecOps Maturity Model (German)](https://vimeo.com/456523229) 44 | * [Online: OWASP DevSecOps Maturity Model - Culture (German)](https://www.meetup.com/de-DE/Breaking-Agile/) 2020-08-25 45 | * [Video: Usage of the OWASP DevSecOps Maturity Model](https://www.youtube.com/watch?v=tX9RHZ_O5NU), [OWASP Ottawa Chapter](https://www.meetup.com/de-DE/OWASP-Ottawa/events/272355636/), 2020-08-17 46 | * [Continuous Application Security Testing for Enterprise](https://docs.google.com/presentation/d/1dAewXIHgBEKHKwBPpM5N_G2eM6PRpduoGJrp6R6pNUI/edit?usp=sharing), DevOps Meetup Hamburg, 2019-09-26 47 | * [DevSecOps Maturity Model](https://docs.google.com/presentation/d/1zF7c_0cPYBO7LHcLNtEApQBB_qJugXgRQUyiwBKKtKk/edit?usp=sharing), Open Security Summit, near London, 2018 48 | * [Security in DevOps-Strategies](https://www.youtube.com/watch?v=gWjGWebWahE&t=448s), 28.09.2017, Hamburg, Germany 49 | * [DevSecOps Maturity Model](https://docs.google.com/presentation/d/1rrbyXqxy3LXAJNPFrVH99mj_BNaJKymMsXZItYArWEM/edit?usp=sharing), 2017 50 | 51 | # Assessment 52 | 53 | In case you would like to perform a DevSecOps assessment, the following tools are available: 54 | 55 | * Usage of the applicaton in a `container`. 56 | * Development of an export to [OWASP Maturity Models](https://github.com/OWASP/Maturity-Models) (recommended for assessments with a lot of teams) 57 | * Creation of your excel sheet (not recommended, you want to use DevOps, don't even try!) 58 | 59 | ## Container 60 | 61 | 1. Install [Docker](https://www.docker.com) 62 | 2. Run `docker pull wurstbrot/dsomm:latest && docker run --rm -p 8080:8080 wurstbrot/dsomm:latest` 63 | 3. Browse to (on macOS and Windows browse to if you are using docker-machine instead 64 | of the native docker installation) 65 | 66 | For customized DSOMM, take a look at https://github.com/wurstbrot/DevSecOps-MaturityModel-custom. 67 | 68 | You can download your current state from the circular heatmap and mount it again via 69 | 70 | ```bash 71 | wget https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/main/src/assets/YAML/generated/generated.yaml # or go to /circular-heatmap and download edited yaml (bottom right) 72 | docker run -p 8080:8080 -v /tmp/generated.yaml:/srv/assets/YAML/generated/generated.yaml wurstbrot/dsomm:latest 73 | ``` 74 | 75 | . 76 | 77 | This approach also allows teams to perform self assessment with changes tracked in a repository. 78 | 79 | ## Amazon EC2 Instance 80 | 81 | 1. In the _EC2_ sidenav select _Instances_ and click _Launch Instance_ 82 | 2. In _Step 1: Choose an Amazon Machine Image (AMI)_ choose an _Amazon 83 | Linux AMI_ or _Amazon Linux 2 AMI_ 84 | 3. In _Step 3: Configure Instance Details_ unfold _Advanced Details_ and 85 | copy the script below into _User Data_ 86 | 4. In _Step 6: Configure Security Group_ add a _Rule_ that opens port 80 87 | for HTTP 88 | 5. Launch your instance 89 | 6. Browse to your instance's public DNS 90 | 91 | ```bash 92 | #!/bin/bash 93 | service docker start 94 | docker run -d -p 80:8080 wurstbrot/dsomm:latest 95 | ``` 96 | 97 | ## Activity Definitions 98 | The definition of the activities are in the [data-repository](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data). 99 | 100 | ## Teams and Groups 101 | To customize these teams, you can create your own [meta.yaml](src/assets/meta.yaml) file with your unique team definitions. 102 | 103 | Assessments within the framework can be based on either a team or a specific application, which can be referred to as the context. Depending on how you define the context or teams, you may want to group them together. 104 | 105 | Here are a couple of examples to illustrate this, in breakers the DSOMM word: 106 | - Multiple applications (teams) can belong to a single overarching team (application). 107 | - Multiple teams (teams) can belong to a larger department (group). 108 | 109 | Feel free to create your own [meta.yaml](src/assets/meta.yaml) file to tailor the framework to your specific needs and mount it in your environment (e.g. kubernetes or docker). 110 | Here is an example to start docker with customized meta.yaml: 111 | ``` 112 | # Customized meta.yaml 113 | cp src/assets/YAML/meta.yaml . 114 | docker run -v $(pwd)/meta.yaml:/srv/assets/YAML/meta.yaml -p 8080:8080 wurstbrot/dsomm 115 | 116 | # Customized meta.yaml and generated.yaml 117 | cp src/assets/YAML/meta.yaml . 118 | cp $(pwd)/src/assets/YAML/generated/generated.yaml . 119 | docker run -v $(pwd)/meta.yaml:/srv/assets/YAML/meta.yaml -v $(pwd)/generated.yaml:/srv/assets/YAML/generated/generated.yaml -p 8080:8080 wurstbrot/dsomm 120 | ``` 121 | 122 | In the corresponding [dimension YAMLs](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data/tree/main/src/assets/YAML/default), use: 123 | ``` 124 | [...] 125 | teamsImplemented: 126 | Default: false 127 | B: true 128 | C: true 129 | teamsEvidence: 130 | B: All team members completed OWASP Secure Coding Dojo training on 2025-01-11. 131 | C: | 132 | The pentest report from 2025 has been split into Jira tasks under 133 | [TODO-123](https://jira.example.com/issues/TODO-123). 134 | 135 | _2025-04-01:_ All fixes of **critical** findings are deployed to production. 136 | ``` 137 | The `|` is yaml syntax to indicate that the evidence spans multiple lines. Markdown 138 | syntax can be used. The evidence is currently visible on the activity from the Matrix page. 139 | 140 | # Back link 141 | 142 | - [OWASP DevSecOps maturity model page](https://dsomm.timo-pagel.de/) 143 | - [OWASP DevSecOps project page](https://owasp.org/www-project-devsecops-maturity-model/) 144 | - [OWASP](https://owasp.org) 145 | 146 | # Your help is needed to perform 147 | 148 | * Adding a manual on how to use DSOMM 149 | * Integration of Incident Response 150 | * DevSecOps Toolchain Categorization 151 | * App Sec Maturity Models Mapping 152 | * CAMS Categorization 153 | * Adding assessment questions 154 | 155 | # Multilanguage support 156 | Multilanguage support is not given currently and not planned. 157 | 158 | # Sponsors 159 | 160 | [![Timo Pagel IT-Consulting](https://raw.githubusercontent.com/DefectDojo/Documentation/master/doc/img/timo-pagel-logo.png)](https://pagel.pro) 161 | 162 | [![Apprio Inc](https://github.com/wurstbrot/DevSecOps-MaturityModel/raw/master-old/assets/images/Apiiro_black_logo.png)](https://apiiro.com/) 163 | 164 | [![Heroku (hosting)](https://github.com/wurstbrot/DevSecOps-MaturityModel/raw/master/src/assets/images/sponsors/heroku.png)](https://www.heroku.com/open-source-credit-program) 165 | 166 | # Donations 167 | 168 | If you are using the model or you are inspired by it, want to help but don't want to create pull requests? You can donate at the [OWASP Project Wiki Page](https://owasp.org/donate/?reponame=www-project-devsecops-maturity-model&title=OWASP+Devsecops+Maturity+Model). Donations might be used for the design of logos/images/design or travels. 169 | 170 | # License 171 | 172 | This program is free software: you can redistribute it and/or modify it under the terms of the [GPL 3](https://www.gnu.org/licenses/) license. 173 | 174 | The intellectual property (content in the _data_ folder) is licensed under Attribution-ShareAlike. 175 | An example attribution by changing the content: 176 | > This work is based on the [OWASP DevSecOps Maturity Model](https://dsomm.timo-pagel.de). 177 | 178 | The OWASP DevSecOps Maturity Model and any contributions are Copyright © by Timo Pagel 2017-2022. 179 | -------------------------------------------------------------------------------- /src/assets/Markdown Files/USAGE.md: -------------------------------------------------------------------------------- 1 | # DSOMM - DevSecOps Maturity Model 2 | 3 | ## What is DSOMM? 4 | DSOMM is a framework that helps organizations to assess, improve and prioritize security activities in their software development cycle. 5 | 6 | DSOMM is a project of the OWASP Foundation. 7 | 8 | ## DSOMM vs OWASP SAMM 9 | [DSOMM](https://dsomm.owasp.org/) and [OWASP SAMM](https://owaspsamm.org/) are both frameworks that share a common goal of improving security. 10 | 11 | **OWASP SAMM** is more focused on the overall maturity of an organization's software assurance and security practices, with a broader scope that includes governance, compliance, risk management, and secure software development. 12 | 13 | SAMM is written by security specialists for security specialists, focusing on security processes across the whole organizations. 14 | 15 | **DSOMM** focuses on activities that integrate security directly into the DevOps workflows. DSOMM takes a more technical approach, going lower in the technology stack it provides a roadmap on how to systematically improve the security in the software development. 16 | 17 | DSOMM is written for technical teams focused on implementing secure software. 18 | 19 | DSOMM has currently has a OWASP Lab status, while SAMM has a Flagship status. 20 | 21 | # How to use this DSOMM site 22 | The DSOMM application is a frontend only application, storing all progress in your local storage in your browser. If you delete your local storage, your progress will be gone, and you cannot share your saved progress with anyone else. 23 | 24 | To do that, you need to install your own local DSOMM application. 25 | 26 | You can export the progress of the different activities as a `generated.yaml` file, which you may import into your own site. 27 | 28 | 29 | ## How to setup your own DSOMM 30 | The DSOMM application can be run as a Docker image, an Amazon EC2 instance, or as a standalone Angular application using NodeJS. Please see [INSTALL.md](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/blob/master/INSTALL.md) for further instructions. 31 | 32 | The DSOMM application is currently still a lightweight frontend only application, without a backend to store changes of progress. Any changes are stored in the browser. However, as above, you can export the `generated.yaml` and update your own site with this. 33 | 34 | 35 | # The DSOMM framework 36 | The DSOMM framework has a number of _activities_ grouped by _dimensions_ and _maturity levels_. E.g. the _Centralized system logging_ is a maturity level 1 activity in the _Logging_ dimension, while _Correlation of security events_ is considered level 5. 37 | 38 | 39 | 40 | ## Before you start 41 | To prepare you for there are some activities that we recommend you do before you start using DSOMM. Getting the stakeholders onboard will ease your path. 42 | 43 | See [Maturity level 0](./usage/maturity-level-0) to learn about the important first steps. 44 | 45 | 46 | ## Dimensions 47 | The DSOMM framework categorizes its activities into dimensions, each representing a key area of the software development lifecycle where security can be integrated and matured. 48 | 49 | Dimensions Overview: 50 | - **Build and Deployment**: Focuses on security practices in the CI/CD pipeline and deployment processes 51 | - **Culture and Organization**: Addresses organizational culture, education, and processes that support security initiatives. 52 | - **Implementation**: Covers secure coding and infrastructure hardening practices. 53 | - **Information Gathering**: Involves gathering data for threat analysis, risk assessment, and metrics collection. 54 | - **Test and Verification**: Focuses on testing practices to validate security measures and ensure continuous improvement. 55 | 56 | For detailed information on each dimension, refer to [Dimensions](./usage/dimensions). 57 | 58 | 59 | 60 | 61 | 62 | ## Evidence 63 | If your CISO requires you to document evidence that an activity is completed, you can edit your `generated.yaml` file as documented in the [README.md](./usage/README) _Teams and Groups_. It is currently not possible to provide evidence directly in the browser. 64 | -------------------------------------------------------------------------------- /src/assets/Markdown Files/maturity-level-0.md: -------------------------------------------------------------------------------- 1 | # Pre-Requirements 2 | 3 | Before you start using DSOMM in your organization, there are a few activities that might help you to implement a better security regime. 4 | 5 | 6 | These pre-requirements are highly based on (mostly copied) 7 | from AppSecure NRW's first level of [security-belts](https://github.com/AppSecure-nrw/security-belts/tree/master/white). 8 | 9 | ## Risk management 10 | Understand what the term _risk_ means in this context. 11 |
Definition of risk 12 | NIST defines risk as: 13 | 14 | > a measure of the extent to which an entity is threatened by a potential circumstance or event, and typically is a function of: 15 | > 1. the adverse impact, or magnitude of harm, that would arise 16 | > if the circumstance or event occurs; and 17 | > 2. the likelihood of occurrence. 18 | 19 | _Source: https://csrc.nist.gov/glossary/term/risk_ 20 |
21 | 22 |
Definition of risk in a information security context 23 | In information security, risks arise from the loss of: 24 | 25 | - confidentiality, 26 | - integrity, 27 | - or availability 28 | 29 | of information or information systems and reflect the 30 | potential adverse impacts to: 31 | 32 | - organizational operations 33 | (including: - mission, - functions, - image, - or reputation), 34 | - organizational assets, 35 | - individuals, 36 | - other organizations 37 | (see [NIST.SP.800-53Ar4](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-53Ar4.pdf). 38 | 39 | A risk then tied to a **threat**, its **probability** and its **impacts**. 40 | 41 | If you are interested in Risk Management frameworks and 42 | strategies, you can start from 43 | [FISMA](https://csrc.nist.gov/Projects/risk-management/). 44 |
45 | 46 |
Definition of risk appetite 47 | Risk appetite is defined as: 48 | 49 | > The types and amount of risk, on a broad level, [an organization] is willing to accept in its pursuit of value 50 | 51 | _Source: https://csrc.nist.gov/glossary/term/risk_appetite_ 52 | 53 | Organizations have different risk appetite. It is important to understand what risks your organization is willing to accept, and which are not acceptable. Understanding this will 54 | - help you translate application security risks for your management 55 | - help you focus on risks that matters the most for your organization 56 |
57 | 58 |
Definition of risk tolerance 59 | Risk tolerance is highly connected to risk appetite. NIST's definition is almost identical to its own definition for risk appetit. 60 | 61 | [ISACA](https://en.wikipedia.org/wiki/ISACA), however, defines risk tolerance as: 62 | 63 | > the acceptable deviation from the level set by the risk appetite and business objectives. 64 | 65 | Explaining that: 66 | 67 | > Risk appetite and risk tolerance can be viewed as the “two sides of the same coin” as they relate to organizational performance over time. Risk appetite is about “taking risk” and risk tolerance is about “controlling risk.” For risk appetite to be adopted successfully in decision making, it must be integrated with control environment of the organization through risk tolerance 68 | 69 | _Source: https://www.isaca.org/resources/news-and-trends/isaca-now-blog/2022/risk-appetite-vs-risk-tolerance-what-is-the-difference_ 70 |
71 | 72 | ## Onboard Product Owner and other managers 73 | 74 | To adopt a DSOMM in a product or a project, it is important to identify 75 | the person or the team which is responsible to ensure 76 | that risk-related considerations reflects the organizational 77 | risk appetite and tolerance 78 | (see [Risk Executive](https://csrc.nist.gov/glossary/term/risk_executive) 79 | for a more complete view). 80 | 81 | Depending on the project, this "Risk Manager" - which in layman's terms 82 | is responsible for judging "risks vs. costs" of the product - 83 | can be the `Project Manager`, the `Product Owner` or else: 84 | it is important that he has the proper risk management 85 | knowledge and, receive a proper training. 86 | 87 | The "Risk Manager" must be convinced that continuously improving 88 | security through DSOMM is an effective way to 89 | to minimize risk and build better products. 90 | 91 | The first steps for deploying DSOMM are then the following: 92 | 93 | 1. identify the persons in charge for risk decisions 94 | 1. ask them about their _risk appetite_ 95 | 1. make them aware of information security risks 96 | - show the impacts of threats and their probability 97 | 1. convince them that security requires _continuous_ efforts 98 | 99 | ### Benefits 100 | 101 | - The "Risk Manager" is aware that all software have security vulnerabilities, 102 | and that the related risks should be minimized 103 | - Knowing the risk appetite helps the organization align its security efforts with its overall strategic goals 104 | - Resources must be allocated to improve security and 105 | to avoid, detect and fix vulnerabilities 106 | - Management can perform well informed risk decisions 107 | - The "Risk Manager" has transparent knowledge on how secure the product is 108 | 109 | ## Get to Know Security Policies 110 | 111 | Identify the security policies of your organization and adhere to them. 112 | 113 | Share with the Security Champion Guild how you perform the required activities 114 | from the policies, so others can benefit from your experience. 115 | 116 | In addition, provide feedback to the policy owner. 117 | 118 | Communicate discrepancies with the defined security policies 119 | to the "Risk Manager" 120 | so that he can take proper measures. 121 | 122 | ### Benefits 123 | 124 | - Adopting security policies addressing threats 125 | simplifies building secure software. 126 | - Basic security risks are handled. 127 | 128 | ## Continuously Improve your Security Belt Rank 129 | 130 | Security is like a big pizza. 131 | You cannot eat it as a whole, 132 | but you can slice it and continuously eat small slices. 133 | 134 | Ensure that the "Risk Manager" continuously prioritizes 135 | the security belt activities for the next belt highly 136 | within the product backlog. 137 | 138 | Security belt activities make "good slices" because they are of reasonable 139 | size and have a defined output. 140 | 141 | Celebrate all your implemented security belt activities! 142 | 143 | ### Benefits 144 | 145 | - The team has time to improve its software security. 146 | - The team's initially high motivation and momentum can be used. 147 | - The Risk Manager has visibility on the investment 148 | and the benefits of security belts. 149 | - The team is improving its software security. 150 | 151 | ## Review Security Belt Activities 152 | 153 | Let the Security Champion Guild review your implementations of security belt 154 | activities (or concepts of these implementations) as soon as possible. 155 | This helps to eradicate misunderstandings of security belt activities early. 156 | 157 | ### Benefits 158 | 159 | - The quality of the implementation increases. 160 | - Successes can be celebrated intermediately. 161 | - Early feedback before the belt assessment. 162 | 163 | ## Utilize Pairing when starting an activity 164 | When implementing a security belt activity, approach a peer 165 | from the Security Champion Guild to get you started. 166 | 167 | ## Benefits 168 | 169 | - Knowledge how to implement security belt activities is spread, 170 | so everyone benefits of prior knowledge. 171 | - Starting to implement security belt activities with guidance is easier. 172 | - The team is improving its software security while avoiding previously 173 | made mistakes. 174 | -------------------------------------------------------------------------------- /src/assets/YAML/generated/README.md: -------------------------------------------------------------------------------- 1 | # Generated YAML Files 2 | 3 | This folder contains the `generated.yaml` file, which is dynamically created during the build process. 4 | It stores configuration settings and other automatically generated data used by the DevSecOps Maturity Model (DSOMM). 5 | 6 | ## **What is `generated.yaml`?** 7 | 8 | - It is a machine-generated file that is **not meant to be manually edited**. 9 | - It helps in **storing configuration settings**, which are loaded at runtime. 10 | - Used by the application to dynamically configure settings. 11 | - This file is generated via the [DevSecOps-MaturityModel-data](https://github.com/devsecopsmaturitymodel/DevSecOps-MaturityModel-data) repository. 12 | -------------------------------------------------------------------------------- /src/assets/YAML/meta.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # Various strings and messages 4 | # 5 | strings: 6 | en: &en 7 | references: 8 | samm2: 9 | label: OWASP SAMM VERSION 2 10 | description: |- 11 | Software Assurance Maturity Model 12 | The Software Assurance Maturity Model (SAMM) is an open framework to help organizations formulate 13 | and implement a strategy for software security that is tailored 14 | to the specific risks facing the organization. 15 | https://owaspsamm.org/blog/2020/01/31/samm2-release/ 16 | iso27001-2017: 17 | label: ISO 27001:2017 18 | description: |- 19 | ISO 27001:2017 20 | iso27001-2022: 21 | label: ISO 27001:2022 22 | description: |- 23 | ISO 27001:2022 24 | labels: ['Very Low', 'Low', 'Medium', 'High', 'Very High'] 25 | KnowledgeLabels: 26 | [ 27 | 'Very Low (one discipline)', 28 | 'Low (one discipline)', 29 | 'Medium (two disciplines)', 30 | 'High (two disciplines)', 31 | 'Very High (three or more disciplines)', 32 | ] 33 | hardness: ['Very soft', 'Soft', 'Medium', 'High', 'Very high'] 34 | maturity_levels: 35 | [ 36 | 'Level 1: Basic understanding of security practices', 37 | 'Level 2: Adoption of basic security practices', 38 | 'Level 3: High adoption of security practices', 39 | 'Level 4: Very high adoption of security practices', 40 | 'Level 5: Advanced deployment of security practices at scale', 41 | ] 42 | # Default team 43 | teams: ['Default', 'B', 'C'] 44 | teamGroups: 45 | GroupA: ['Default', 'B'] 46 | GroupB: ['B', 'C'] 47 | GroupC: ['Default', 'C'] 48 | -------------------------------------------------------------------------------- /src/assets/YAML/schemas/dsomm-implementations-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Schema for Implementations", 4 | "type": "object", 5 | "properties": { 6 | "implementations": { 7 | "type": "object", 8 | "patternProperties": { 9 | ".*": { 10 | "type": "object", 11 | "properties": { 12 | "uuid": { 13 | "type": "string", 14 | "format": "uuid" 15 | }, 16 | "name": { 17 | "type": "string", 18 | "default": "" 19 | }, 20 | "tags": { 21 | "type": "array", 22 | "items": { 23 | "type": "string", 24 | "default": "" 25 | } 26 | }, 27 | "url": { 28 | "type": "string", 29 | "format": "uri", 30 | "default": "https://" 31 | }, 32 | "description": { 33 | "type": "string", 34 | "default": "" 35 | } 36 | }, 37 | "required": [ 38 | "uuid", 39 | "name", 40 | "tags", 41 | "url", 42 | "description" 43 | ], 44 | "additionalProperties": false 45 | } 46 | }, 47 | "additionalProperties": true 48 | } 49 | }, 50 | "required": [ 51 | "implementations" 52 | ], 53 | "additionalProperties": false 54 | } -------------------------------------------------------------------------------- /src/assets/YAML/schemas/dsomm-schema-build-and-deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Schema for DSOMM Dimension Build and Deployment", 4 | "type": "object", 5 | "properties": { 6 | "Build and Deployment": { 7 | "type": "object", 8 | "patternProperties": { 9 | "^.*$": { 10 | "type": "object", 11 | "patternProperties": { 12 | "^.*$": { 13 | "type": "object", 14 | "properties": { 15 | "uuid": { 16 | "type": "string", 17 | "format": "uuid" 18 | }, 19 | "description": { 20 | "type": "string" 21 | }, 22 | "risk": { 23 | "type": "string" 24 | }, 25 | "measure": { 26 | "type": "string" 27 | }, 28 | "meta": { 29 | "type": "object", 30 | "properties": { 31 | "implementationGuide": { 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "implementationGuide" 37 | ] 38 | }, 39 | "difficultyOfImplementation": { 40 | "type": "object", 41 | "properties": { 42 | "knowledge": { 43 | "type": "number" 44 | }, 45 | "time": { 46 | "type": "number" 47 | }, 48 | "resources": { 49 | "type": "number" 50 | } 51 | }, 52 | "required": [ 53 | "knowledge", 54 | "time", 55 | "resources" 56 | ] 57 | }, 58 | "usefulness": { 59 | "type": "number" 60 | }, 61 | "level": { 62 | "type": "number" 63 | }, 64 | "assessment": { 65 | "type": "string" 66 | }, 67 | "implementation": { 68 | "type": "array", 69 | "items": { 70 | "type": "object", 71 | "properties": { 72 | "$ref": { 73 | "type": "string", 74 | "format": "uri-reference" 75 | } 76 | }, 77 | "required": [ 78 | "$ref" 79 | ], 80 | "additionalProperties": false 81 | } 82 | }, 83 | "dependsOn": { 84 | "type": "array", 85 | "items": { 86 | "type": "string" 87 | } 88 | }, 89 | "references": { 90 | "type": "object", 91 | "properties": { 92 | "samm2": { 93 | "type": "array", 94 | "items": { 95 | "anyOf": [ 96 | { 97 | "type": "string" 98 | }, 99 | { 100 | "type": "number" 101 | } 102 | ] 103 | } 104 | }, 105 | "iso27001-2017": { 106 | "type": "array", 107 | "items": { 108 | "anyOf": [ 109 | { 110 | "type": "string" 111 | }, 112 | { 113 | "type": "number" 114 | } 115 | ] 116 | } 117 | }, 118 | "iso27001-2022": { 119 | "type": "array", 120 | "items": { 121 | "anyOf": [ 122 | { 123 | "type": "string" 124 | }, 125 | { 126 | "type": "number" 127 | } 128 | ] 129 | } 130 | } 131 | }, 132 | "required": [ 133 | "samm2", 134 | "iso27001-2017", 135 | "iso27001-2022" 136 | ] 137 | }, 138 | "isImplemented": { 139 | "type": "boolean" 140 | }, 141 | "evidence": { 142 | "type": "string" 143 | }, 144 | "comments": { 145 | "type": "string" 146 | } 147 | }, 148 | "required": [ 149 | "uuid", 150 | "description", 151 | "risk", 152 | "measure", 153 | "difficultyOfImplementation", 154 | "usefulness", 155 | "level", 156 | "implementation", 157 | "references", 158 | "isImplemented", 159 | "evidence", 160 | "comments" 161 | ], 162 | "additionalProperties": false 163 | } 164 | } 165 | } 166 | } 167 | } 168 | }, 169 | "required": [ 170 | "Build and Deployment" 171 | ] 172 | } -------------------------------------------------------------------------------- /src/assets/YAML/schemas/dsomm-schema-culture-and-organization.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Schema for DSOMM Dimension Culture and Organization", 4 | "type": "object", 5 | "properties": { 6 | "Culture and Organization": { 7 | "type": "object", 8 | "patternProperties": { 9 | "^.*$": { 10 | "type": "object", 11 | "patternProperties": { 12 | "^.*$": { 13 | "type": "object", 14 | "properties": { 15 | "uuid": { 16 | "type": "string", 17 | "format": "uuid" 18 | }, 19 | "description": { 20 | "type": "string" 21 | }, 22 | "risk": { 23 | "type": "string" 24 | }, 25 | "measure": { 26 | "type": "string" 27 | }, 28 | "meta": { 29 | "type": "object", 30 | "properties": { 31 | "implementationGuide": { 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "implementationGuide" 37 | ] 38 | }, 39 | "difficultyOfImplementation": { 40 | "type": "object", 41 | "properties": { 42 | "knowledge": { 43 | "type": "number" 44 | }, 45 | "time": { 46 | "type": "number" 47 | }, 48 | "resources": { 49 | "type": "number" 50 | } 51 | }, 52 | "required": [ 53 | "knowledge", 54 | "time", 55 | "resources" 56 | ] 57 | }, 58 | "usefulness": { 59 | "type": "number" 60 | }, 61 | "level": { 62 | "type": "number" 63 | }, 64 | "assessment": { 65 | "type": "string" 66 | }, 67 | "implementation": { 68 | "type": "array", 69 | "items": { 70 | "type": "object", 71 | "properties": { 72 | "$ref": { 73 | "type": "string", 74 | "format": "uri-reference" 75 | } 76 | }, 77 | "required": [ 78 | "$ref" 79 | ], 80 | "additionalProperties": false 81 | } 82 | }, 83 | "dependsOn": { 84 | "type": "array", 85 | "items": { 86 | "type": "string" 87 | } 88 | }, 89 | "references": { 90 | "type": "object", 91 | "properties": { 92 | "samm2": { 93 | "type": "array", 94 | "items": { 95 | "anyOf": [ 96 | { 97 | "type": "string" 98 | }, 99 | { 100 | "type": "number" 101 | } 102 | ] 103 | } 104 | }, 105 | "iso27001-2017": { 106 | "type": "array", 107 | "items": { 108 | "anyOf": [ 109 | { 110 | "type": "string" 111 | }, 112 | { 113 | "type": "number" 114 | } 115 | ] 116 | } 117 | }, 118 | "iso27001-2022": { 119 | "type": "array", 120 | "items": { 121 | "anyOf": [ 122 | { 123 | "type": "string" 124 | }, 125 | { 126 | "type": "number" 127 | } 128 | ] 129 | } 130 | } 131 | }, 132 | "required": [ 133 | "samm2", 134 | "iso27001-2017", 135 | "iso27001-2022" 136 | ] 137 | }, 138 | "isImplemented": { 139 | "type": "boolean" 140 | }, 141 | "evidence": { 142 | "type": "string" 143 | }, 144 | "comments": { 145 | "type": "string" 146 | } 147 | }, 148 | "required": [ 149 | "uuid", 150 | "description", 151 | "risk", 152 | "measure", 153 | "difficultyOfImplementation", 154 | "usefulness", 155 | "level", 156 | "implementation", 157 | "references", 158 | "isImplemented", 159 | "evidence", 160 | "comments" 161 | ], 162 | "additionalProperties": false 163 | } 164 | } 165 | } 166 | } 167 | } 168 | }, 169 | "required": [ 170 | "Culture and Organization" 171 | ] 172 | } -------------------------------------------------------------------------------- /src/assets/YAML/schemas/dsomm-schema-implementation.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Schema for DSOMM Dimension Implementation", 4 | "type": "object", 5 | "properties": { 6 | "Implementation": { 7 | "type": "object", 8 | "patternProperties": { 9 | "^.*$": { 10 | "type": "object", 11 | "patternProperties": { 12 | "^.*$": { 13 | "type": "object", 14 | "properties": { 15 | "uuid": { 16 | "type": "string", 17 | "format": "uuid" 18 | }, 19 | "description": { 20 | "type": "string" 21 | }, 22 | "risk": { 23 | "type": "string" 24 | }, 25 | "measure": { 26 | "type": "string" 27 | }, 28 | "meta": { 29 | "type": "object", 30 | "properties": { 31 | "implementationGuide": { 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "implementationGuide" 37 | ] 38 | }, 39 | "difficultyOfImplementation": { 40 | "type": "object", 41 | "properties": { 42 | "knowledge": { 43 | "type": "number" 44 | }, 45 | "time": { 46 | "type": "number" 47 | }, 48 | "resources": { 49 | "type": "number" 50 | } 51 | }, 52 | "required": [ 53 | "knowledge", 54 | "time", 55 | "resources" 56 | ] 57 | }, 58 | "usefulness": { 59 | "type": "number" 60 | }, 61 | "level": { 62 | "type": "number" 63 | }, 64 | "assessment": { 65 | "type": "string" 66 | }, 67 | "implementation": { 68 | "type": "array", 69 | "items": { 70 | "type": "object", 71 | "properties": { 72 | "$ref": { 73 | "type": "string", 74 | "format": "uri-reference" 75 | } 76 | }, 77 | "required": [ 78 | "$ref" 79 | ], 80 | "additionalProperties": false 81 | } 82 | }, 83 | "dependsOn": { 84 | "type": "array", 85 | "items": { 86 | "type": "string" 87 | } 88 | }, 89 | "references": { 90 | "type": "object", 91 | "properties": { 92 | "samm2": { 93 | "type": "array", 94 | "items": { 95 | "anyOf": [ 96 | { 97 | "type": "string" 98 | }, 99 | { 100 | "type": "number" 101 | } 102 | ] 103 | } 104 | }, 105 | "iso27001-2017": { 106 | "type": "array", 107 | "items": { 108 | "anyOf": [ 109 | { 110 | "type": "string" 111 | }, 112 | { 113 | "type": "number" 114 | } 115 | ] 116 | } 117 | }, 118 | "iso27001-2022": { 119 | "type": "array", 120 | "items": { 121 | "anyOf": [ 122 | { 123 | "type": "string" 124 | }, 125 | { 126 | "type": "number" 127 | } 128 | ] 129 | } 130 | } 131 | }, 132 | "required": [ 133 | "samm2", 134 | "iso27001-2017", 135 | "iso27001-2022" 136 | ] 137 | }, 138 | "isImplemented": { 139 | "type": "boolean" 140 | }, 141 | "evidence": { 142 | "type": "string" 143 | }, 144 | "comments": { 145 | "type": "string" 146 | } 147 | }, 148 | "required": [ 149 | "uuid", 150 | "description", 151 | "risk", 152 | "measure", 153 | "difficultyOfImplementation", 154 | "usefulness", 155 | "level", 156 | "implementation", 157 | "references", 158 | "isImplemented", 159 | "evidence", 160 | "comments" 161 | ], 162 | "additionalProperties": false 163 | } 164 | } 165 | } 166 | } 167 | } 168 | }, 169 | "required": [ 170 | "Implementation" 171 | ] 172 | } -------------------------------------------------------------------------------- /src/assets/YAML/schemas/dsomm-schema-information-gathering.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Schema for DSOMM Dimension Information Gathering", 4 | "type": "object", 5 | "properties": { 6 | "Information Gathering": { 7 | "type": "object", 8 | "patternProperties": { 9 | "^.*$": { 10 | "type": "object", 11 | "patternProperties": { 12 | "^.*$": { 13 | "type": "object", 14 | "properties": { 15 | "uuid": { 16 | "type": "string", 17 | "format": "uuid" 18 | }, 19 | "description": { 20 | "type": "string" 21 | }, 22 | "risk": { 23 | "type": "string" 24 | }, 25 | "measure": { 26 | "type": "string" 27 | }, 28 | "meta": { 29 | "type": "object", 30 | "properties": { 31 | "implementationGuide": { 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "implementationGuide" 37 | ] 38 | }, 39 | "difficultyOfImplementation": { 40 | "type": "object", 41 | "properties": { 42 | "knowledge": { 43 | "type": "number" 44 | }, 45 | "time": { 46 | "type": "number" 47 | }, 48 | "resources": { 49 | "type": "number" 50 | } 51 | }, 52 | "required": [ 53 | "knowledge", 54 | "time", 55 | "resources" 56 | ] 57 | }, 58 | "usefulness": { 59 | "type": "number" 60 | }, 61 | "level": { 62 | "type": "number" 63 | }, 64 | "assessment": { 65 | "type": "string" 66 | }, 67 | "implementation": { 68 | "type": "array", 69 | "items": { 70 | "type": "object", 71 | "properties": { 72 | "$ref": { 73 | "type": "string", 74 | "format": "uri-reference" 75 | } 76 | }, 77 | "required": [ 78 | "$ref" 79 | ], 80 | "additionalProperties": false 81 | } 82 | }, 83 | "dependsOn": { 84 | "type": "array", 85 | "items": { 86 | "type": "string" 87 | } 88 | }, 89 | "references": { 90 | "type": "object", 91 | "properties": { 92 | "samm2": { 93 | "type": "array", 94 | "items": { 95 | "anyOf": [ 96 | { 97 | "type": "string" 98 | }, 99 | { 100 | "type": "number" 101 | } 102 | ] 103 | } 104 | }, 105 | "iso27001-2017": { 106 | "type": "array", 107 | "items": { 108 | "anyOf": [ 109 | { 110 | "type": "string" 111 | }, 112 | { 113 | "type": "number" 114 | } 115 | ] 116 | } 117 | }, 118 | "iso27001-2022": { 119 | "type": "array", 120 | "items": { 121 | "anyOf": [ 122 | { 123 | "type": "string" 124 | }, 125 | { 126 | "type": "number" 127 | } 128 | ] 129 | } 130 | } 131 | }, 132 | "required": [ 133 | "samm2", 134 | "iso27001-2017", 135 | "iso27001-2022" 136 | ] 137 | }, 138 | "isImplemented": { 139 | "type": "boolean" 140 | }, 141 | "evidence": { 142 | "type": "string" 143 | }, 144 | "comments": { 145 | "type": "string" 146 | } 147 | }, 148 | "required": [ 149 | "uuid", 150 | "description", 151 | "risk", 152 | "measure", 153 | "difficultyOfImplementation", 154 | "usefulness", 155 | "level", 156 | "implementation", 157 | "references", 158 | "isImplemented", 159 | "evidence", 160 | "comments" 161 | ], 162 | "additionalProperties": false 163 | } 164 | } 165 | } 166 | } 167 | } 168 | }, 169 | "required": [ 170 | "Information Gathering" 171 | ] 172 | } -------------------------------------------------------------------------------- /src/assets/YAML/schemas/dsomm-schema-test-and-verification.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Schema for DSOMM Dimension Test and Verification", 4 | "type": "object", 5 | "properties": { 6 | "Test and Verification": { 7 | "type": "object", 8 | "patternProperties": { 9 | "^.*$": { 10 | "type": "object", 11 | "patternProperties": { 12 | "^.*$": { 13 | "type": "object", 14 | "properties": { 15 | "uuid": { 16 | "type": "string", 17 | "format": "uuid" 18 | }, 19 | "description": { 20 | "type": "string" 21 | }, 22 | "risk": { 23 | "type": "string" 24 | }, 25 | "measure": { 26 | "type": "string" 27 | }, 28 | "meta": { 29 | "type": "object", 30 | "properties": { 31 | "implementationGuide": { 32 | "type": "string" 33 | } 34 | }, 35 | "required": [ 36 | "implementationGuide" 37 | ] 38 | }, 39 | "difficultyOfImplementation": { 40 | "type": "object", 41 | "properties": { 42 | "knowledge": { 43 | "type": "number" 44 | }, 45 | "time": { 46 | "type": "number" 47 | }, 48 | "resources": { 49 | "type": "number" 50 | } 51 | }, 52 | "required": [ 53 | "knowledge", 54 | "time", 55 | "resources" 56 | ] 57 | }, 58 | "usefulness": { 59 | "type": "number" 60 | }, 61 | "level": { 62 | "type": "number" 63 | }, 64 | "assessment": { 65 | "type": "string" 66 | }, 67 | "implementation": { 68 | "type": "array", 69 | "items": { 70 | "type": "object", 71 | "properties": { 72 | "$ref": { 73 | "type": "string", 74 | "format": "uri-reference" 75 | } 76 | }, 77 | "required": [ 78 | "$ref" 79 | ], 80 | "additionalProperties": false 81 | } 82 | }, 83 | "dependsOn": { 84 | "type": "array", 85 | "items": { 86 | "type": "string" 87 | } 88 | }, 89 | "references": { 90 | "type": "object", 91 | "properties": { 92 | "samm2": { 93 | "type": "array", 94 | "items": { 95 | "anyOf": [ 96 | { 97 | "type": "string" 98 | }, 99 | { 100 | "type": "number" 101 | } 102 | ] 103 | } 104 | }, 105 | "iso27001-2017": { 106 | "type": "array", 107 | "items": { 108 | "anyOf": [ 109 | { 110 | "type": "string" 111 | }, 112 | { 113 | "type": "number" 114 | } 115 | ] 116 | } 117 | }, 118 | "iso27001-2022": { 119 | "type": "array", 120 | "items": { 121 | "anyOf": [ 122 | { 123 | "type": "string" 124 | }, 125 | { 126 | "type": "number" 127 | } 128 | ] 129 | } 130 | } 131 | }, 132 | "required": [ 133 | "samm2", 134 | "iso27001-2017", 135 | "iso27001-2022" 136 | ] 137 | }, 138 | "isImplemented": { 139 | "type": "boolean" 140 | }, 141 | "evidence": { 142 | "type": "string" 143 | }, 144 | "comments": { 145 | "type": "string" 146 | } 147 | }, 148 | "required": [ 149 | "uuid", 150 | "description", 151 | "risk", 152 | "measure", 153 | "difficultyOfImplementation", 154 | "usefulness", 155 | "level", 156 | "implementation", 157 | "references", 158 | "isImplemented", 159 | "evidence", 160 | "comments" 161 | ], 162 | "additionalProperties": false 163 | } 164 | } 165 | } 166 | } 167 | } 168 | }, 169 | "required": [ 170 | "Test and Verification" 171 | ] 172 | } -------------------------------------------------------------------------------- /src/assets/YAML/teams.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # Teams 4 | # 5 | teams: ['A', 'B', 'C'] 6 | teamGroups: 7 | AB: ['A', 'B'] 8 | BC: ['B', 'C'] 9 | AC: ['A', 'C'] 10 | -------------------------------------------------------------------------------- /src/assets/images/Build and Deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/Build and Deployment.png -------------------------------------------------------------------------------- /src/assets/images/Culture and Organization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/Culture and Organization.png -------------------------------------------------------------------------------- /src/assets/images/Implementation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/Implementation.png -------------------------------------------------------------------------------- /src/assets/images/Information Gathering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/Information Gathering.png -------------------------------------------------------------------------------- /src/assets/images/Test and Verification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/Test and Verification.png -------------------------------------------------------------------------------- /src/assets/images/logo-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/logo-image.png -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/assets/images/sponsors/heroku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/sponsors/heroku.png -------------------------------------------------------------------------------- /src/assets/images/userday/Brook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/userday/Brook.png -------------------------------------------------------------------------------- /src/assets/images/userday/Francesco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/userday/Francesco.png -------------------------------------------------------------------------------- /src/assets/images/userday/Jannik.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/userday/Jannik.jpg -------------------------------------------------------------------------------- /src/assets/images/userday/Timo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/images/userday/Timo.png -------------------------------------------------------------------------------- /src/assets/presentations/userday-sf-2024-reach-your-dynamic-depth-with-owasp-securecodebox.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/assets/presentations/userday-sf-2024-reach-your-dynamic-depth-with-owasp-securecodebox.pdf -------------------------------------------------------------------------------- /src/custom-theme.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | 3 | // ---------------------------------------------- 4 | // Theme Colors and Typography 5 | // ---------------------------------------------- 6 | $light-theme: ( 7 | background: white, 8 | text: black, 9 | link: blue, 10 | ); 11 | 12 | $custom-dark-theme: ( 13 | background: #2c2c2c, 14 | text: #e0e0e0, 15 | link: #bb86fc, 16 | ); 17 | 18 | $custom-typography: mat.define-typography-level( 19 | $font-family: montserrat, 20 | $font-weight: 400, 21 | $font-size: 1rem, 22 | $line-height: 1, 23 | $letter-spacing: normal 24 | ); 25 | @include mat.core($custom-typography); 26 | 27 | // ---------------------------------------------- 28 | // Angular Material Palettes 29 | // ---------------------------------------------- 30 | $DSOMM-primary: mat.define-palette(mat.$green-palette, 400); 31 | $DSOMM-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); 32 | $DSOMM-warn: mat.define-palette(mat.$red-palette); 33 | 34 | // ---------------------------------------------- 35 | // Angular Material Themes 36 | // ---------------------------------------------- 37 | $DSOMM-light-theme: mat.define-light-theme(( 38 | color: ( 39 | primary: $DSOMM-primary, 40 | accent: $DSOMM-accent, 41 | warn: $DSOMM-warn 42 | ) 43 | )); 44 | 45 | $DSOMM-dark-theme: mat.define-dark-theme(( 46 | color: ( 47 | primary: $DSOMM-primary, 48 | accent: $DSOMM-accent, 49 | warn: $DSOMM-warn 50 | ) 51 | )); 52 | 53 | // ---------------------------------------------- 54 | // Base Theme Mixin 55 | // ---------------------------------------------- 56 | @mixin apply-theme($theme) { 57 | background-color: map-get($theme, background); 58 | color: map-get($theme, text); 59 | 60 | a { 61 | color: map-get($theme, link); 62 | } 63 | a:visited { 64 | color: map-get($theme, visited-link); 65 | } 66 | } 67 | 68 | // ---------------------------------------------- 69 | // Light Mode Styles 70 | // ---------------------------------------------- 71 | body { 72 | 73 | .title-button, 74 | h1, h2, h3, h4, h5, h6 { 75 | color: map-get($light-theme, text); 76 | } 77 | } 78 | 79 | .light-theme { 80 | --heatmap-filled: #4caf50; 81 | --heatmap-disabled: #dddddd; 82 | --heatmap-cursor: green; 83 | --heatmap-background: white; 84 | --heatmap-stroke: black; 85 | --heatmap-cursor-selected:var(--heatmap-cursor); 86 | --heatmap-cursor-hover: transparent; 87 | 88 | @include mat.all-component-themes($DSOMM-light-theme); 89 | } 90 | 91 | // ---------------------------------------------- 92 | // Dark Mode Styles 93 | // ---------------------------------------------- 94 | body.dark-theme { 95 | @include apply-theme($custom-dark-theme); 96 | @include mat.all-component-themes($DSOMM-dark-theme); 97 | 98 | --heatmap-filled: #007700; 99 | --heatmap-disabled: #666666; 100 | --heatmap-cursor: green; 101 | --heatmap-background: #bbbbbb; 102 | --heatmap-stroke: #000000; 103 | --heatmap-cursor-selected:var(--heatmap-cursor); 104 | --heatmap-cursor-hover: transparent; 105 | 106 | .title-button, 107 | h1, h2, h3, h4, h5, h6 { 108 | color: map-get($custom-dark-theme, text); 109 | } 110 | 111 | // General properties 112 | p, li, tr { 113 | color: #e0e0e0; 114 | } 115 | 116 | // Common containers 117 | mat-card, 118 | .mat-dialog-container, 119 | .mat-expansion-panel, 120 | .mat-accordion, 121 | .overlay-wrapper { 122 | background-color: #2c2c2c; 123 | color: #e0e0e0; 124 | } 125 | 126 | // Dialog styling 127 | .mat-dialog-container { 128 | border: 1px solid #444; 129 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.7); 130 | } 131 | 132 | // Modal override 133 | .overlay-modal { 134 | background-color: #2c2c2c; 135 | color: #e0e0e0; 136 | border-radius: 6px; 137 | 138 | mat-card { 139 | background-color: transparent; 140 | } 141 | 142 | h1, h2, h3, h4, h5, h6 { 143 | color: #e0e0e0; 144 | } 145 | } 146 | 147 | // Circular heatmap (radar chart) 148 | .circular-heat text, 149 | .labels.segment text { 150 | fill: #ffffff; 151 | } 152 | 153 | .circular-heat line, 154 | .circular-heat path { 155 | stroke: var(--heatmap-stroke); 156 | } 157 | 158 | .mat-chip.mat-standard-chip { 159 | color: #ababab; 160 | } 161 | 162 | .mat-chip.mat-standard-chip.mat-chip-selected.mat-primary { 163 | background-color: #74b277; 164 | } 165 | } 166 | 167 | @include mat.all-component-themes($DSOMM-dark-theme); 168 | 169 | .button-container { 170 | display: flex; 171 | flex-direction: column; // Vertical alignment 172 | gap: 10px; // Space between buttons 173 | } 174 | 175 | svg .cursors path { 176 | fill: transparent; 177 | pointer-events: none; 178 | } 179 | 180 | svg .cursors #hover { 181 | stroke: var(--heatmap-cursor); 182 | stroke-width: 7px; 183 | } 184 | 185 | svg .cursors #selected { 186 | stroke: var(--heatmap-cursor-selected, #000000); // optional fallback 187 | stroke-width: 7px; 188 | } 189 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsecopsmaturitymodel/DevSecOps-MaturityModel/87d6312e606239c972bcdaa997d9db18bcbbb901/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DSOMM 6 | 7 | 8 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | const savedTheme = localStorage.getItem('theme') || 'light'; 2 | document.body.classList.remove('light-theme', 'dark-theme'); 3 | document.body.classList.add(`${savedTheme}-theme`); 4 | console.log('[main.ts] Theme set to:', savedTheme); // 5 | 6 | import { enableProdMode } from '@angular/core'; 7 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 8 | 9 | import { AppModule } from './app/app.module'; 10 | import { environment } from './environments/environment'; 11 | 12 | if (environment.production) { 13 | enableProdMode(); 14 | } 15 | 16 | platformBrowserDynamic() 17 | .bootstrapModule(AppModule) 18 | .catch(err => console.error(err)); 19 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /*************************************************************************************************** 2 | * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. 3 | */ 4 | import '@angular/localize/init'; 5 | /** 6 | * This file includes polyfills needed by Angular and is loaded before the app. 7 | * You can add your own extra polyfills to this file. 8 | * 9 | * This file is divided into 2 sections: 10 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 11 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 12 | * file. 13 | * 14 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 15 | * automatically update themselves. This includes recent versions of Safari, Chrome (including 16 | * Opera), Edge on the desktop, and iOS and Chrome on mobile. 17 | * 18 | * Learn more in https://angular.io/guide/browser-support 19 | */ 20 | 21 | /*************************************************************************************************** 22 | * BROWSER POLYFILLS 23 | */ 24 | 25 | /** 26 | * By default, zone.js will patch all possible macroTask and DomEvents 27 | * user can disable parts of macroTask/DomEvents patch by setting following flags 28 | * because those flags need to be set before `zone.js` being loaded, and webpack 29 | * will put import in the top of bundle, so user need to create a separate file 30 | * in this directory (for example: zone-flags.ts), and put the following flags 31 | * into that file, and then add the following code before importing zone.js. 32 | * import './zone-flags'; 33 | * 34 | * The flags allowed in zone-flags.ts are listed here. 35 | * 36 | * The following flags will work for all browsers. 37 | * 38 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 39 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 40 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 41 | * 42 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 43 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 44 | * 45 | * (window as any).__Zone_enable_cross_context_check = true; 46 | * 47 | */ 48 | 49 | /*************************************************************************************************** 50 | * Zone JS is required by default for Angular itself. 51 | */ 52 | import 'zone.js'; // Included with Angular CLI. 53 | 54 | /*************************************************************************************************** 55 | * APPLICATION IMPORTS 56 | */ 57 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | html, body { height: 100%; } 4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } 5 | 6 | .userday table :is(td, th) { 7 | border: 1px solid black; 8 | padding: 0.3em; 9 | } 10 | 11 | .dark-theme .userday table :is(td, th) { 12 | border-color: #e0e0e0; 13 | } 14 | .dark-theme .userday tr:nth-child(even) { 15 | background-color: #365d36; 16 | } 17 | 18 | .userday tr:nth-child(even) { 19 | background-color: #66bb6a; 20 | } 21 | 22 | .userday img { 23 | max-height: 100px; 24 | float: left; 25 | margin-right: 10px; 26 | } 27 | 28 | .usage-dimensions img { 29 | max-width: 40rem; 30 | } -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting, 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context( 12 | path: string, 13 | deep?: boolean, 14 | filter?: RegExp 15 | ): { 16 | (id: string): T; 17 | keys(): string[]; 18 | }; 19 | }; 20 | 21 | // First, initialize the Angular testing environment. 22 | getTestBed().initTestEnvironment( 23 | BrowserDynamicTestingModule, 24 | platformBrowserDynamicTesting() 25 | ); 26 | 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "es2017", 20 | "module": "es2020", 21 | "lib": [ 22 | "es2020", 23 | "dom" 24 | ] 25 | }, 26 | "angularCompilerOptions": { 27 | "enableI18nLegacyMessageIdFormat": false, 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | --------------------------------------------------------------------------------