├── .env ├── .env.dist ├── .github ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md └── workflows │ └── non-regression.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── api ├── .env-dist ├── .env.test ├── .gitignore ├── .php-cs-fixer.dist.php ├── Dockerfile ├── behat.yml.dist ├── bin │ └── console ├── composer.json ├── composer.lock ├── config │ ├── bootstrap.php │ ├── bundles.php │ ├── packages │ │ ├── api_platform.yaml │ │ ├── cache.yaml │ │ ├── dev │ │ │ ├── hautelook_alice.yaml │ │ │ ├── nelmio_alice.yaml │ │ │ ├── routing.yaml │ │ │ └── web_profiler.yaml │ │ ├── doctrine.yaml │ │ ├── doctrine_migrations.yaml │ │ ├── framework.yaml │ │ ├── lexik_jwt_authentication.yaml │ │ ├── mercure.yaml │ │ ├── nelmio_cors.yaml │ │ ├── prod │ │ │ ├── api_platform.yaml │ │ │ ├── doctrine.yaml │ │ │ └── routing.yaml │ │ ├── routing.yaml │ │ ├── security.yaml │ │ ├── stof_doctrine_extensions.yaml │ │ ├── test │ │ │ ├── doctrine.yaml │ │ │ ├── framework.yaml │ │ │ ├── hautelook_alice.yaml │ │ │ ├── nelmio_alice.yaml │ │ │ ├── routing.yaml │ │ │ ├── twig.yaml │ │ │ ├── validator.yaml │ │ │ └── web_profiler.yaml │ │ ├── translation.yaml │ │ ├── twig.yaml │ │ ├── validator.yaml │ │ └── vich_uploader.yaml │ ├── preload.php │ ├── routes.yaml │ ├── routes │ │ ├── annotations.yaml │ │ ├── api_platform.yaml │ │ └── dev │ │ │ ├── framework.yaml │ │ │ ├── twig.yaml │ │ │ └── web_profiler.yaml │ ├── services.yaml │ └── services_test.yaml ├── docker │ ├── nginx │ │ └── conf.d │ │ │ └── default.conf │ ├── php │ │ ├── conf.d │ │ │ ├── api-platform.dev.ini │ │ │ └── api-platform.prod.ini │ │ ├── docker-entrypoint.sh │ │ └── docker-healthcheck.sh │ └── varnish │ │ └── conf │ │ └── default.vcl ├── features │ ├── host.feature │ ├── impact.feature │ └── step.feature ├── fixtures │ ├── .gitignore │ ├── client.yaml │ ├── host.yaml │ ├── host_vuln.yaml │ ├── impact.yaml │ ├── mission.yaml │ ├── mission_type.yaml │ ├── point.yaml │ ├── step.yaml │ ├── user.yaml │ ├── vuln.yaml │ ├── vuln_translation.yaml │ └── vuln_type.yaml ├── generateJWT.sh ├── helm │ └── api │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── ingress.yaml │ │ ├── nginx-deployment.yaml │ │ ├── nginx-service.yaml │ │ ├── php-deployment.yaml │ │ ├── php-service.yaml │ │ ├── secrets.yaml │ │ ├── serviceaccount.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ ├── varnish-deployment.yaml │ │ └── varnish-service.yaml │ │ └── values.yaml ├── hosts.txt ├── migrations │ └── .gitignore ├── phpunit.xml.dist ├── public │ ├── favicon.ico │ ├── index.php │ └── media │ │ └── .gitignore ├── src │ ├── Constraint │ │ ├── Ip.php │ │ └── IpValidator.php │ ├── Controller │ │ ├── .gitignore │ │ ├── CreateMediaObjectAction.php │ │ ├── MediaController.php │ │ ├── UploadHostController.php │ │ └── UploadNmapController.php │ ├── Entity │ │ ├── .gitignore │ │ ├── Client.php │ │ ├── Host.php │ │ ├── HostVuln.php │ │ ├── Impact.php │ │ ├── MediaObject.php │ │ ├── Mission.php │ │ ├── MissionType.php │ │ ├── NegativePoint.php │ │ ├── Nmap.php │ │ ├── PositivePoint.php │ │ ├── Step.php │ │ ├── Translation │ │ │ ├── AbstractTranslation.php │ │ │ └── VulnTranslation.php │ │ ├── User.php │ │ ├── Vuln.php │ │ └── VulnType.php │ ├── EventSubscriber │ │ └── ResolveMediaObjectContentUrlSubscriber.php │ ├── Extension │ │ └── AssignedMissionExtension.php │ ├── Filter │ │ └── MissionTypeFilter.php │ ├── Kernel.php │ ├── Listener │ │ ├── LocaleListener.php │ │ ├── PasswordEncoderListener.php │ │ └── UserCreateListener.php │ ├── Migrations │ │ └── .gitignore │ ├── Repository │ │ ├── .gitignore │ │ ├── ClientRepository.php │ │ ├── HostRepository.php │ │ ├── HostVulnRepository.php │ │ ├── ImpactRepository.php │ │ ├── MissionRepository.php │ │ ├── NegativePointRepository.php │ │ ├── NmapRepository.php │ │ ├── PositivePointRepository.php │ │ ├── StepRepository.php │ │ ├── UserRepository.php │ │ └── VulnRepository.php │ ├── Serializer │ │ ├── MediaObjectNormalizer.php │ │ ├── TranslationContextBuilder.php │ │ └── TranslationNormalizer.php │ ├── Subscriber │ │ └── JWTGenerationSubscriber.php │ ├── Traits │ │ ├── DescriptionTrait.php │ │ ├── IdTrait.php │ │ ├── LocaleTrait.php │ │ ├── NameTrait.php │ │ ├── RemediationTrait.php │ │ ├── Translatable │ │ │ ├── TranslatableDescriptionTrait.php │ │ │ ├── TranslatableNameTrait.php │ │ │ └── TranslatableRemediationTrait.php │ │ └── TypeTrait.php │ └── Voter │ │ ├── MissionVoter.php │ │ └── UserVoter.php ├── symfony.lock ├── templates │ ├── base.html.twig │ ├── upload_host │ │ └── index.html.twig │ └── upload_nmap │ │ └── index.html.twig ├── tests │ ├── Behat │ │ ├── APIContext.php │ │ ├── ClientContext.php │ │ ├── ClientTrait.php │ │ ├── DemoContext.php │ │ ├── FeatureContext.php │ │ ├── HostContext.php │ │ ├── ImpactContext.php │ │ └── StepContext.php │ └── bootstrap.php └── translations │ └── .gitignore ├── client ├── .dockerignore ├── .eslintrc ├── .gitignore ├── Dockerfile ├── README.md ├── angular.json ├── cypress.json ├── cypress │ ├── .gitignore │ ├── fixtures │ │ └── data.json │ ├── integration │ │ ├── dashboard.spec.js │ │ ├── helpers.js │ │ ├── login.spec.js │ │ ├── missions.spec.js │ │ └── vulns.spec.js │ ├── plugins │ │ └── index.js │ └── support │ │ ├── commands │ │ ├── generic │ │ │ ├── form.js │ │ │ ├── index.js │ │ │ └── list.js │ │ └── index.js │ │ └── index.js ├── docker │ └── nginx.conf ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── components │ │ │ ├── add-vulns-to-host-external │ │ │ │ ├── add-vulns-to-host-external.component.html │ │ │ │ ├── add-vulns-to-host-external.component.scss │ │ │ │ └── add-vulns-to-host-external.component.ts │ │ │ ├── clients │ │ │ │ ├── clientsCreate.component.ts │ │ │ │ ├── clientsEdit.component.ts │ │ │ │ ├── clientsList.component.ts │ │ │ │ └── index.ts │ │ │ ├── conclusion │ │ │ │ ├── conclusion.component.html │ │ │ │ ├── conclusion.component.scss │ │ │ │ └── conclusion.component.ts │ │ │ ├── edit-vuln-with-state │ │ │ │ ├── edit-vuln-with-state.component.html │ │ │ │ ├── edit-vuln-with-state.component.scss │ │ │ │ └── edit-vuln-with-state.component.ts │ │ │ ├── errors │ │ │ │ ├── errors.component.html │ │ │ │ ├── errors.component.scss │ │ │ │ └── errors.component.ts │ │ │ ├── generic │ │ │ │ ├── form │ │ │ │ │ ├── generic-create.component.ts │ │ │ │ │ ├── generic-edit.component.ts │ │ │ │ │ ├── generic-form.component.html │ │ │ │ │ ├── generic-form.component.ts │ │ │ │ │ ├── queryable-input.component.html │ │ │ │ │ └── queryable-input.component.ts │ │ │ │ ├── index.ts │ │ │ │ └── list │ │ │ │ │ ├── generic-list.component.html │ │ │ │ │ └── generic-list.component.ts │ │ │ ├── homepage │ │ │ │ ├── homepage.component.html │ │ │ │ ├── homepage.component.scss │ │ │ │ └── homepage.component.ts │ │ │ ├── hosts │ │ │ │ ├── hostsEdit.component.ts │ │ │ │ ├── hostsList.component.ts │ │ │ │ └── index.ts │ │ │ ├── impacts │ │ │ │ ├── impactsCreate.component.ts │ │ │ │ ├── impactsEdit.component.ts │ │ │ │ ├── impactsList.component.ts │ │ │ │ └── index.ts │ │ │ ├── login │ │ │ │ ├── login.component.html │ │ │ │ ├── login.component.scss │ │ │ │ └── login.component.ts │ │ │ ├── mission-my │ │ │ │ ├── mission-my.component.html │ │ │ │ ├── mission-my.component.scss │ │ │ │ └── mission-my.component.ts │ │ │ ├── mission-single │ │ │ │ ├── mission-single.component.html │ │ │ │ ├── mission-single.component.scss │ │ │ │ └── mission-single.component.ts │ │ │ ├── missions │ │ │ │ ├── index.ts │ │ │ │ ├── missionsCreate.component.ts │ │ │ │ ├── missionsEdit.component.ts │ │ │ │ └── missionsList.component.ts │ │ │ ├── popup │ │ │ │ ├── popup.component.html │ │ │ │ ├── popup.component.scss │ │ │ │ └── popup.component.ts │ │ │ ├── side-bar │ │ │ │ ├── side-bar.component.html │ │ │ │ ├── side-bar.component.scss │ │ │ │ └── side-bar.component.ts │ │ │ ├── users │ │ │ │ ├── index.ts │ │ │ │ ├── usersCreate.component.ts │ │ │ │ ├── usersEdit.component.ts │ │ │ │ └── usersList.component.ts │ │ │ └── vulns │ │ │ │ ├── index.ts │ │ │ │ ├── vulnsCreate.component.ts │ │ │ │ ├── vulnsEdit.component.ts │ │ │ │ └── vulnsList.component.ts │ │ ├── file-information.ts │ │ ├── form │ │ │ ├── Date.ts │ │ │ ├── Input.ts │ │ │ ├── Queryable.ts │ │ │ └── index.ts │ │ ├── guard │ │ │ ├── auth.guard.ts │ │ │ ├── index.ts │ │ │ └── role.guard.ts │ │ ├── helpers │ │ │ └── translation.ts │ │ ├── interceptor │ │ │ ├── index.ts │ │ │ └── unauthorized.interceptor.ts │ │ ├── model │ │ │ ├── AbstractType.ts │ │ │ ├── Authentication.ts │ │ │ ├── Client.ts │ │ │ ├── Host.ts │ │ │ ├── HostVuln.ts │ │ │ ├── Impact.ts │ │ │ ├── Media.ts │ │ │ ├── Mission.ts │ │ │ ├── Point.ts │ │ │ ├── Step.ts │ │ │ ├── Translation.ts │ │ │ ├── User.ts │ │ │ ├── Vuln.ts │ │ │ ├── VulnTranslation.ts │ │ │ └── abstract.ts │ │ ├── resources │ │ │ ├── AbstractResource.ts │ │ │ ├── ClientResource.ts │ │ │ ├── HostResource.ts │ │ │ ├── HostVulnResource.ts │ │ │ ├── ImpactResource.ts │ │ │ ├── MissionResource.ts │ │ │ ├── UserResource.ts │ │ │ └── VulnResource.ts │ │ ├── router │ │ │ ├── ClientRouter.ts │ │ │ ├── DashboardRouter.ts │ │ │ ├── HostRouter.ts │ │ │ ├── HostVulnRouter.ts │ │ │ ├── ImpactRouter.ts │ │ │ ├── MissionRouter.ts │ │ │ ├── UserRouter.ts │ │ │ ├── VulnRouter.ts │ │ │ └── router.ts │ │ ├── security │ │ │ └── isGranted.ts │ │ ├── services │ │ │ ├── abstract.ts │ │ │ ├── clients.service.ts │ │ │ ├── configService.ts │ │ │ ├── connection.service.ts │ │ │ ├── filter.service.ts │ │ │ ├── hostVulns.service.ts │ │ │ ├── hosts.service.ts │ │ │ ├── impacts.service.ts │ │ │ ├── locale.service.ts │ │ │ ├── mediaObjects.service.ts │ │ │ ├── medias.service.ts │ │ │ ├── missions.service.ts │ │ │ ├── negative.service.ts │ │ │ ├── positive.service.ts │ │ │ ├── steps.service.ts │ │ │ ├── theme.service.ts │ │ │ ├── types.service.ts │ │ │ ├── uploads.service.ts │ │ │ ├── users.service.ts │ │ │ ├── vulnTranslations.service.ts │ │ │ ├── vulnTypes.service.ts │ │ │ └── vulns.service.ts │ │ └── storage │ │ │ ├── AbstractStorage.ts │ │ │ ├── Locale.ts │ │ │ ├── Theme.ts │ │ │ └── Token.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── Smersh.docx │ │ ├── burp.json │ │ ├── flags │ │ │ ├── ar.png │ │ │ ├── en.png │ │ │ ├── es.png │ │ │ ├── fr.png │ │ │ ├── it.png │ │ │ ├── jp.png │ │ │ ├── ru.png │ │ │ └── ua.png │ │ ├── i18n │ │ │ ├── ar.json │ │ │ ├── en.json │ │ │ ├── es.json │ │ │ ├── fr.json │ │ │ ├── it.json │ │ │ ├── jp.json │ │ │ ├── ru.json │ │ │ └── ua.json │ │ └── logo.png │ ├── environments │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ ├── test.ts │ └── theme.scss ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ├── docker-compose.yml ├── docker └── dev-tls │ └── Dockerfile ├── img ├── api.png ├── createMission.png ├── demo.gif ├── hacktivity.png ├── homepage.png ├── rapport-preview.png ├── rapport-preview2.png ├── searchbar.png └── showMission.png ├── logo.png ├── smersh-body.png ├── smersh.jpg ├── traefik.toml └── update-deps.sh /.env: -------------------------------------------------------------------------------- 1 | API_DB_NAME=api 2 | API_DB_PASS=!ChangeMe! 3 | API_DB_USER=api-platform 4 | APP_ENV=dev 5 | CODI_DB_NAME=codimd 6 | CODI_DB_PASS=change_password 7 | CODI_DB_USER=codimd 8 | DOMAIN=smersh.lan 9 | JWT_KEY=secret-key 10 | SUBDOMAINS=(api|bitwarden|codimd) 11 | TRANSPORT=https:// 12 | -------------------------------------------------------------------------------- /.env.dist: -------------------------------------------------------------------------------- 1 | API_DB_NAME=api 2 | API_DB_PASS=!ChangeMe! 3 | API_DB_USER=api-platform 4 | APP_ENV=dev 5 | CODI_DB_NAME=codimd 6 | CODI_DB_PASS=change_password 7 | CODI_DB_USER=codimd 8 | DOMAIN=smersh.lan 9 | JWT_KEY=secret-key 10 | SUBDOMAINS=(api|bitwarden|codimd) 11 | TRANSPORT=https:// 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jenaye 4 | custom: https://www.buymeacoffee.com/smersh 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | | Q | A 2 | | ------------- | --- 3 | | Branch? | 4 | | Bug fix? | yes/no 5 | | New feature? | yes/no 6 | | Deprecations? | yes/no 7 | | Tickets | Fix #... -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # How to report 2 | 3 | ``` 4 | Hello, 5 | if you find a security bug, don't hesitate to report it to the following email address: cmepw@protonmail.com 6 | 7 | I would ask you to specify the version used as well as a scenario of exploitation (the steps 1 by 1, requests by requests) 8 | 9 | Thank you in advance, for taking the time to read this message to help us to improve this project 10 | 11 | ``` 12 | -------------------------------------------------------------------------------- /.github/workflows/non-regression.yml: -------------------------------------------------------------------------------- 1 | name: Build container and validate lint/tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | checkout-code-and-validate: 7 | name: Checkout code 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v2 12 | - name: Add SMERSH hosts to /etc/hosts 13 | run: | 14 | sudo echo "127.0.0.1 smersh.lan api.smersh.lan codimd.smersh.lan bitwarden.smersh.lan" | sudo tee -a /etc/hosts 15 | - name: Prepare the stack to build 16 | run: cp api/.env-dist api/.env 17 | - name: Composer install 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: 7.4 21 | tools: composer 22 | - name: Install PHP dependencies 23 | run: cd api && composer install 24 | - name: Create the SMERSH network 25 | run: docker network create smersh || true 26 | - name: Install docker stack and populate database with fixtures 27 | run: make create-network up 28 | - name: Populate database with fixtures 29 | run: make reset-db jwt cache 30 | - uses: actions/setup-node@v2 31 | with: 32 | node-version: 15 33 | cache: npm 34 | cache-dependency-path: client/package-lock.json 35 | - run: npm install 36 | working-directory: client 37 | - name: Run e2e tests 38 | run: npm run e2e 39 | working-directory: client 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | */.env 3 | .env.local 4 | data/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ca Devrait Le Daire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMERSH 2 | 3 | ![License](https://img.shields.io/badge/License-MIT-darkred.svg) 4 | ![GitHub stars](https://img.shields.io/github/stars/CMEPW/Smersh) 5 | [![Rawsec's CyberSecurity Inventory](https://inventory.raw.pm/img/badges/Rawsec-inventoried-FF5050_flat.svg)](https://inventory.raw.pm/tools.html#rawsec_cli) 6 | [![Black Hat Arsenal](https://img.shields.io/badge/Blackhat%20Arsenal-EUROPE%202021-blue)](https://img.shields.io/badge/Blackhat%20Arsenal-EUROPE%202021-blue) 7 | 8 |

9 | logo 10 |

11 | 12 | Smersh is a pentest oriented collaborative tool used to track the progress of your company's missions and generate rapport. 13 | 14 | # Preview front (Angular): 15 | 16 | ![demo](img/demo.gif) 17 | 18 | # Documentation 19 | 20 | All information is available at the following address: [https://cmepw.github.io/documentation/](https://matro7sh.github.io/documentation/) 21 | 22 | 23 | # How to contribute ? 24 | 25 | Just *fork* repository then create branch, work and push your content + create PR 26 | 27 | ``` 28 | git checkout -b MyBranch 29 | git add -p 30 | git commit -m "xx" 31 | git push origin MyBranch 32 | ``` 33 | 34 | # Contributors 35 | 36 | - darkweak - https://github.com/darkweak 37 | - michmich1000 - https://michmich.eu 38 | - SilouFr - https://github.com/SilouFr 39 | - sanchis - https://github.com/sanchis 40 | - RayMoDev - https://github.com/RayMoDev 41 | - Linda - https://github.com/proglin 42 | - Log_s - https://rmrf-logs.com/man-log_s/ 43 | - Swiftoui - https://github.com/Swiftoui 44 | - noraj - https://pwn.by/noraj/ 45 | 46 | 47 | # Available languages : 48 | 49 | - italian 50 | - Japanese 51 | - French 52 | - Spanish 53 | - Algerian 54 | - Ukranian 55 | - Russian 56 | -------------------------------------------------------------------------------- /api/.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_SECRET='$ecretf0rt3st' 4 | SYMFONY_DEPRECATIONS_HELPER=999999 5 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | config/jwt/*.pem 3 | ###> friends-of-behat/symfony-extension ### 4 | /behat.yml 5 | ###< friends-of-behat/symfony-extension ### 6 | 7 | ###> phpunit/phpunit ### 8 | /phpunit.xml 9 | .phpunit.result.cache 10 | ###< phpunit/phpunit ### 11 | 12 | public/media/** 13 | 14 | ###> symfony/framework-bundle ### 15 | /.env.local 16 | /.env.local.php 17 | /.env.*.local 18 | /config/secrets/prod/prod.decrypt.private.php 19 | /public/bundles/ 20 | /var/ 21 | /vendor/ 22 | ###< symfony/framework-bundle ### 23 | 24 | ###> friendsofphp/php-cs-fixer ### 25 | /.php-cs-fixer.php 26 | /.php-cs-fixer.cache 27 | ###< friendsofphp/php-cs-fixer ### 28 | 29 | ###> lexik/jwt-authentication-bundle ### 30 | /config/jwt/*.pem 31 | ###< lexik/jwt-authentication-bundle ### 32 | -------------------------------------------------------------------------------- /api/.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 5 | ->exclude('var') 6 | ; 7 | 8 | return (new PhpCsFixer\Config()) 9 | ->setRules([ 10 | '@Symfony' => true, 11 | ]) 12 | ->setFinder($finder) 13 | ->setCacheFile('.php-cs-fixer.cache') // forward compatibility with 3.x line 14 | ; 15 | -------------------------------------------------------------------------------- /api/behat.yml.dist: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | impact: 4 | paths: [ '%paths.base%/features/impact.feature' ] 5 | contexts: 6 | - App\Tests\Behat\ClientContext: 7 | kernel: '@kernel' 8 | - App\Tests\Behat\DemoContext: 9 | kernel: '@kernel' 10 | - App\Tests\Behat\FeatureContext 11 | - App\Tests\Behat\ImpactContext: 12 | kernel: '@kernel' 13 | - Behatch\Context\JsonContext 14 | host: 15 | paths: [ '%paths.base%/features/host.feature' ] 16 | contexts: 17 | - App\Tests\Behat\ClientContext: 18 | kernel: '@kernel' 19 | - App\Tests\Behat\DemoContext: 20 | kernel: '@kernel' 21 | - App\Tests\Behat\FeatureContext 22 | - App\Tests\Behat\HostContext: 23 | kernel: '@kernel' 24 | - Behatch\Context\JsonContext 25 | step: 26 | paths: [ '%paths.base%/features/step.feature' ] 27 | contexts: 28 | - App\Tests\Behat\ClientContext: 29 | kernel: '@kernel' 30 | - App\Tests\Behat\DemoContext: 31 | kernel: '@kernel' 32 | - App\Tests\Behat\FeatureContext 33 | - App\Tests\Behat\StepContext: 34 | kernel: '@kernel' 35 | - Behatch\Context\JsonContext 36 | 37 | 38 | extensions: 39 | FriendsOfBehat\SymfonyExtension: 40 | kernel: 41 | environment: test 42 | Behatch\Extension: ~ 43 | Behat\MinkExtension: 44 | sessions: 45 | default: 46 | symfony: ~ 47 | -------------------------------------------------------------------------------- /api/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 23 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 24 | } 25 | 26 | if ($input->hasParameterOption('--no-debug', true)) { 27 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 28 | } 29 | 30 | require dirname(__DIR__).'/config/bootstrap.php'; 31 | 32 | if ($_SERVER['APP_DEBUG']) { 33 | umask(0000); 34 | 35 | if (class_exists(Debug::class)) { 36 | Debug::enable(); 37 | } 38 | } 39 | 40 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 41 | $application = new Application($kernel); 42 | $application->run($input); 43 | -------------------------------------------------------------------------------- /api/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2) 13 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) { 14 | (new Dotenv(false))->populate($env); 15 | } else { 16 | // load all the .env files 17 | (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env'); 18 | } 19 | 20 | $_SERVER += $_ENV; 21 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 22 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 23 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 24 | -------------------------------------------------------------------------------- /api/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 6 | Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], 7 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 8 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 9 | ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], 10 | Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], 11 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 12 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 13 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 14 | Vich\UploaderBundle\VichUploaderBundle::class => ['all' => true], 15 | Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], 16 | Locastic\ApiPlatformTranslationBundle\ApiPlatformTranslationBundle::class => ['all' => true], 17 | Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], 18 | FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true], 19 | Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true], 20 | Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true], 21 | Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'test' => true], 22 | ]; 23 | -------------------------------------------------------------------------------- /api/config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | title: Smersh 3 | version: 1.2.0 4 | mapping: 5 | paths: ['%kernel.project_dir%/src/Entity'] 6 | patch_formats: 7 | json: ['application/merge-patch+json'] 8 | swagger: 9 | versions: [3] 10 | api_keys: 11 | apiKey: 12 | name: Authorization 13 | type: header 14 | # Mercure integration, remove if unwanted 15 | mercure: 16 | hub_url: '%env(MERCURE_SUBSCRIBE_URL)%' 17 | collection: 18 | pagination: 19 | maximum_items_per_page: 50 20 | client_items_per_page: true 21 | -------------------------------------------------------------------------------- /api/config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Unique name of your app: used to compute stable namespaces for cache keys. 4 | #prefix_seed: your_vendor_name/app_name 5 | 6 | # The "app" cache stores to the filesystem by default. 7 | # The data in this cache should persist between deploys. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: null 20 | -------------------------------------------------------------------------------- /api/config/packages/dev/hautelook_alice.yaml: -------------------------------------------------------------------------------- 1 | hautelook_alice: 2 | fixtures_path: fixtures 3 | -------------------------------------------------------------------------------- /api/config/packages/dev/nelmio_alice.yaml: -------------------------------------------------------------------------------- 1 | nelmio_alice: 2 | functions_blacklist: 3 | - 'current' 4 | - 'shuffle' 5 | - 'date' 6 | - 'time' 7 | - 'file' 8 | - 'md5' 9 | - 'sha1' 10 | -------------------------------------------------------------------------------- /api/config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /api/config/packages/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { only_exceptions: false } 7 | -------------------------------------------------------------------------------- /api/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | url: '%env(resolve:DATABASE_URL)%' 4 | 5 | # IMPORTANT: You MUST configure your server version, 6 | # either here or in the DATABASE_URL env var (see .env file) 7 | server_version: '12' 8 | orm: 9 | auto_generate_proxy_classes: true 10 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 11 | auto_mapping: true 12 | mappings: 13 | App: 14 | is_bundle: false 15 | type: annotation 16 | dir: '%kernel.project_dir%/src/Entity' 17 | prefix: 'App\Entity' 18 | alias: App 19 | translatable: 20 | type: annotation 21 | alias: Gedmo 22 | prefix: Gedmo\Translatable\Entity 23 | dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity" 24 | -------------------------------------------------------------------------------- /api/config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | dir_name: '%kernel.project_dir%/src/Migrations' 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | namespace: DoctrineMigrations 6 | -------------------------------------------------------------------------------- /api/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #csrf_protection: true 4 | #http_method_override: true 5 | 6 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 7 | # Remove or comment this section to explicitly disable session support. 8 | session: 9 | handler_id: null 10 | cookie_secure: auto 11 | cookie_samesite: lax 12 | 13 | #esi: true 14 | #fragments: true 15 | php_errors: 16 | log: true 17 | -------------------------------------------------------------------------------- /api/config/packages/lexik_jwt_authentication.yaml: -------------------------------------------------------------------------------- 1 | lexik_jwt_authentication: 2 | secret_key: '%env(resolve:JWT_SECRET_KEY)%' 3 | public_key: '%env(resolve:JWT_PUBLIC_KEY)%' 4 | pass_phrase: '%env(JWT_PASSPHRASE)%' 5 | token_ttl: 3600 6 | -------------------------------------------------------------------------------- /api/config/packages/mercure.yaml: -------------------------------------------------------------------------------- 1 | mercure: 2 | enable_profiler: '%kernel.debug%' 3 | hubs: 4 | default: 5 | url: '%env(MERCURE_PUBLISH_URL)%' 6 | jwt: '%env(MERCURE_JWT_TOKEN)%' 7 | -------------------------------------------------------------------------------- /api/config/packages/nelmio_cors.yaml: -------------------------------------------------------------------------------- 1 | nelmio_cors: 2 | defaults: 3 | origin_regex: true 4 | allow_origin: ['*'] 5 | allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] 6 | allow_headers: ['*'] 7 | expose_headers: ['Link'] 8 | max_age: 3600 9 | paths: 10 | '^/': null 11 | -------------------------------------------------------------------------------- /api/config/packages/prod/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | # Varnish integration, remove if unwanted 3 | http_cache: 4 | invalidation: 5 | enabled: true 6 | varnish_urls: ['%env(VARNISH_URL)%'] 7 | max_age: 0 8 | shared_max_age: 3600 9 | vary: ['Content-Type', 'Authorization', 'Origin'] 10 | public: true 11 | -------------------------------------------------------------------------------- /api/config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: pool 6 | pool: doctrine.system_cache_pool 7 | query_cache_driver: 8 | type: pool 9 | pool: doctrine.system_cache_pool 10 | result_cache_driver: 11 | type: pool 12 | pool: doctrine.result_cache_pool 13 | 14 | framework: 15 | cache: 16 | pools: 17 | doctrine.result_cache_pool: 18 | adapter: cache.app 19 | doctrine.system_cache_pool: 20 | adapter: cache.system 21 | -------------------------------------------------------------------------------- /api/config/packages/prod/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: null 4 | -------------------------------------------------------------------------------- /api/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | utf8: true 4 | -------------------------------------------------------------------------------- /api/config/packages/stof_doctrine_extensions.yaml: -------------------------------------------------------------------------------- 1 | # Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html 2 | # See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/ 3 | stof_doctrine_extensions: 4 | default_locale: en_US 5 | -------------------------------------------------------------------------------- /api/config/packages/test/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | # "TEST_TOKEN" is typically set by ParaTest 4 | dbname: 'main_test%env(default::TEST_TOKEN)%' 5 | -------------------------------------------------------------------------------- /api/config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /api/config/packages/test/hautelook_alice.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: ../dev/hautelook_alice.yaml } 3 | -------------------------------------------------------------------------------- /api/config/packages/test/nelmio_alice.yaml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: ../dev/nelmio_alice.yaml } 3 | -------------------------------------------------------------------------------- /api/config/packages/test/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /api/config/packages/test/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | strict_variables: true 3 | -------------------------------------------------------------------------------- /api/config/packages/test/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | not_compromised_password: false 4 | -------------------------------------------------------------------------------- /api/config/packages/test/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: false 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { collect: false } 7 | -------------------------------------------------------------------------------- /api/config/packages/translation.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | default_locale: en 3 | translator: 4 | default_path: '%kernel.project_dir%/translations' 5 | fallbacks: 6 | - en 7 | - fr 8 | -------------------------------------------------------------------------------- /api/config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: '%kernel.project_dir%/templates' 3 | -------------------------------------------------------------------------------- /api/config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | 5 | # Enables validator auto-mapping support. 6 | # For instance, basic validation constraints will be inferred from Doctrine's metadata. 7 | #auto_mapping: 8 | # App\Entity\: [] 9 | -------------------------------------------------------------------------------- /api/config/packages/vich_uploader.yaml: -------------------------------------------------------------------------------- 1 | vich_uploader: 2 | db_driver: orm 3 | 4 | mappings: 5 | media_object: 6 | uri_prefix: /media 7 | upload_destination: '%kernel.project_dir%/public/media' 8 | # Will rename uploaded files using a uniqueid as a prefix. 9 | namer: Vich\UploaderBundle\Naming\OrignameNamer 10 | -------------------------------------------------------------------------------- /api/config/preload.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1; do 31 | sleep 1 32 | done 33 | 34 | if ls -A src/Migrations/*.php > /dev/null 2>&1; then 35 | bin/console doctrine:migrations:migrate --no-interaction 36 | fi 37 | fi 38 | 39 | exec docker-php-entrypoint "$@" 40 | -------------------------------------------------------------------------------- /api/docker/php/docker-healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | export SCRIPT_NAME=/ping 5 | export SCRIPT_FILENAME=/ping 6 | export REQUEST_METHOD=GET 7 | 8 | if cgi-fcgi -bind -connect 127.0.0.1:9000; then 9 | exit 0 10 | fi 11 | 12 | exit 1 13 | -------------------------------------------------------------------------------- /api/fixtures/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/fixtures/.gitignore -------------------------------------------------------------------------------- /api/fixtures/client.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\Client: 2 | client (template): 3 | phone: 06 XX XX XX XX 4 | 5 | not_apple (extends client): 6 | firstName: Steve 7 | lastName: Jobs 8 | mail: contact@not_apple.com 9 | name: not apple 10 | 11 | blue_bird (extends client): 12 | firstName: Jack 13 | lastName: Dorsey 14 | mail: contact@blue_bird.com 15 | name: blue bird 16 | -------------------------------------------------------------------------------- /api/fixtures/host.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\Host: 2 | host_ca_devrait_le_faire: 3 | name: https://cadevraitlefaire.fr 4 | mission: '@internal_mission_1' 5 | checked: true 6 | 7 | host_jenaye: 8 | name: https://jenaye.fr 9 | mission: '@internal_mission_0' 10 | checked: true 11 | technology: ReactJS 12 | 13 | host_souin: 14 | name: https://github.com/Darkweak/Souin 15 | mission: '@internal_mission_0' 16 | checked: false 17 | -------------------------------------------------------------------------------- /api/fixtures/host_vuln.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\HostVuln: 2 | host_vuln_jenaye: 3 | currentState: We just found Sqli on jenaye.fr 4 | host: '@host_jenaye' 5 | impact: '@impact_Low' 6 | vuln: '@sqli' 7 | 8 | host_vuln_souin: 9 | currentState: We just found Sqli on jenaye.fr 10 | host: '@host_souin' 11 | impact: '@impact_Medium' 12 | vuln: '@xss' 13 | -------------------------------------------------------------------------------- /api/fixtures/impact.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\Impact: 2 | impact_{Low, Medium, High, Critical}: 3 | name: 4 | -------------------------------------------------------------------------------- /api/fixtures/mission.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\Mission: 2 | mission (template): 3 | clients: [] 4 | endDate: 5 | name: Default mission name 6 | nmapFiler: false 7 | nessus: false 8 | nessusFiler: false 9 | nmap: false 10 | startDate: 11 | users: [] 12 | 13 | internal_mission_0 (extends mission): 14 | clients: 15 | - '@not_apple' 16 | credentials: http://path_to_credentials 17 | name: FAME MISSION WEB 18 | nmap: true 19 | pathToCodi: https://codimd.smersh.lan/path_to_codi 20 | users: 21 | - '@user_pentester' 22 | 23 | internal_mission_1 (extends mission): 24 | clients: 25 | - '@blue_bird' 26 | name: FAKE MISSION 2 27 | users: 28 | - '@user_pentester' 29 | - '@jenaye' 30 | -------------------------------------------------------------------------------- /api/fixtures/mission_type.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\MissionType: 2 | external: 3 | name: Web Penetration Test 4 | 5 | internal: 6 | name: Infrastructure Penetration Test 7 | -------------------------------------------------------------------------------- /api/fixtures/step.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\Step: 2 | step (template): 3 | createdAt: '' 4 | findAt: '' 5 | 6 | step_0 (extends step): 7 | mission: '@internal_mission_0' 8 | description: 9 | -------------------------------------------------------------------------------- /api/fixtures/user.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\User: 2 | user (template): 3 | city: russia 4 | enabled: true 5 | phone: 06 XX YY ZZ AA 6 | roles: ['ROLE_USER'] 7 | 8 | user_{admin, client, manager, pentester, guest} (extends user): 9 | mail: '\@smersh.app' 10 | password: 11 | trigram: ', 0, 3)>' 12 | roles: ['ROLE_)>'] 13 | username: 14 | 15 | jenaye (extends user): 16 | mail: jenaye@protonmail.com 17 | password: jenaye 18 | roles: ['ROLE_ADMIN'] 19 | trigram: jen 20 | username: jenaye 21 | 22 | unassigned_pentester (extends user): 23 | mail: unassigned_pentester@smersh.app 24 | password: unassigned_pentester 25 | roles: ['ROLE_PENTESTER'] 26 | trigram: una 27 | username: unassigned_pentester 28 | -------------------------------------------------------------------------------- /api/fixtures/vuln_type.yaml: -------------------------------------------------------------------------------- 1 | App\Entity\VulnType: 2 | vuln_type_0: 3 | name: Web Penetration Test 4 | 5 | vuln_type_1: 6 | name: Infrastructure Penetration Test 7 | -------------------------------------------------------------------------------- /api/generateJWT.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker-compose exec -T php sh -c ' 3 | set -e 4 | apk add openssl 5 | mkdir -p config/jwt 6 | jwt_passphrase="$(echo $JWT_PASSPHRASE)" 7 | [ -z "$jwt_passphrase" ] && echo "Loading JWT_PASSPHRASE from .env file" && jwt_passphrase=${JWT_PASSPHRASE:-$(grep ''^JWT_PASSPHRASE='' .env | cut -f 2 -d ''='')} 8 | echo "$jwt_passphrase" | openssl genpkey -out config/jwt/private.pem -pass stdin -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096 9 | echo "$jwt_passphrase" | openssl pkey -in config/jwt/private.pem -passin stdin -out config/jwt/public.pem -pubout 10 | (setfacl -R -m u:www-data:rX -m u:"$(whoami)":rwX config/jwt && setfacl -dR -m u:www-data:rX -m u:"$(whoami)":rwX config/jwt) || chmod -R 755 config/jwt 11 | ' 12 | -------------------------------------------------------------------------------- /api/helm/api/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /api/helm/api/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 0.1.0 3 | description: A Helm chart for an API Platform API 4 | name: api 5 | version: 0.1.0 6 | home: https://api-platform.com 7 | icon: https://api-platform.com/logo-250x250.png 8 | dependencies: 9 | - name: postgresql 10 | version: ~8.6.0 11 | repository: https://charts.bitnami.com/bitnami 12 | condition: postgresql.enabled 13 | - name: mercure 14 | version: ~3.0.0 15 | repository: https://kubernetes-charts.storage.googleapis.com/ 16 | condition: mercure.enabled 17 | -------------------------------------------------------------------------------- /api/helm/api/README.md: -------------------------------------------------------------------------------- 1 | # Deploying to a Kubernetes Cluster 2 | 3 | API Platform comes with a native integration with [Kubernetes](https://kubernetes.io/) and the [Helm](https://helm.sh/) 4 | package manager. 5 | 6 | [Learn how to deploy in the dedicated documentation entry](https://api-platform.com/docs/deployment/kubernetes/). 7 | -------------------------------------------------------------------------------- /api/helm/api/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "api.fullname" . }} 5 | labels: 6 | {{- include "api.labels" . | nindent 4 }} 7 | data: 8 | env: {{ .Values.php.env | quote }} 9 | debug: {{ .Values.php.debug | quote }} 10 | cors-allow-origin: {{ .Values.php.corsAllowOrigin | quote }} 11 | varnish-url: {{ if .Values.varnish.url }}{{ .Values.varnish.url | quote }}{{ else }}http://varnish{{ end }} 12 | trusted-hosts: {{ .Values.php.trustedHosts | quote }} 13 | trusted-proxies: {{ join "," .Values.php.trustedProxies }} 14 | mercure-publish-url: {{ .Values.mercure.publishUrl | quote }} 15 | mercure-subscribe-url: {{ .Values.mercure.subscribeUrl | quote }} 16 | -------------------------------------------------------------------------------- /api/helm/api/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "api.fullname" . -}} 3 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 4 | apiVersion: networking.k8s.io/v1beta1 5 | {{- else -}} 6 | apiVersion: extensions/v1beta1 7 | {{- end }} 8 | kind: Ingress 9 | metadata: 10 | name: {{ $fullName }} 11 | labels: 12 | {{- include "api.labels" . | nindent 4 }} 13 | {{- with .Values.ingress.annotations }} 14 | annotations: 15 | {{- toYaml . | nindent 4 }} 16 | {{- end }} 17 | spec: 18 | {{- if .Values.ingress.tls }} 19 | tls: 20 | {{- range .Values.ingress.tls }} 21 | - hosts: 22 | {{- range .hosts }} 23 | - {{ . | quote }} 24 | {{- end }} 25 | secretName: {{ .secretName }} 26 | {{- end }} 27 | {{- end }} 28 | rules: 29 | {{- range .Values.ingress.hosts }} 30 | - host: {{ .host | quote }} 31 | http: 32 | paths: 33 | {{- range .paths }} 34 | - path: {{ . }} 35 | backend: 36 | {{- if $.Values.varnish.enabled }} 37 | serviceName: {{ $fullName }}-varnish 38 | servicePort: {{ $.Values.varnish.service.port }} 39 | {{- else }} 40 | serviceName: {{ $fullName }}-nginx 41 | servicePort: {{ $.Values.nginx.service.port }} 42 | {{- end }} 43 | {{- end }} 44 | {{- end }} 45 | {{- end }} 46 | -------------------------------------------------------------------------------- /api/helm/api/templates/nginx-service.yaml: -------------------------------------------------------------------------------- 1 | {{- $name := "nginx" -}} 2 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | # /!\ To be reachable by Varnish, the service name MUST be hardcoded. 7 | # /!\ To deploy several instances in the same namespace you MUST rename the service here and in api/docker/varnish/conf/default.vcl 8 | name: {{ if .Values.varnish.enabled }}api{{ else }}{{ include "api.fullname" . }}-{{ $name }}{{ end }} 9 | labels: 10 | {{- include "api.labels" $data | nindent 4 }} 11 | spec: 12 | type: {{ .Values.nginx.service.type }} 13 | ports: 14 | - port: {{ .Values.nginx.service.port }} 15 | targetPort: http 16 | protocol: TCP 17 | name: http 18 | selector: 19 | {{- include "api.selectorLabels" $data | nindent 4 }} 20 | -------------------------------------------------------------------------------- /api/helm/api/templates/php-service.yaml: -------------------------------------------------------------------------------- 1 | {{- $name := "php" -}} 2 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: {{ include "api.fullname" . }}-{{ $name }} 7 | labels: 8 | {{- include "api.labels" $data | nindent 4 }} 9 | spec: 10 | type: {{ .Values.php.service.type }} 11 | ports: 12 | - port: {{ .Values.php.service.port }} 13 | selector: 14 | {{- include "api.selectorLabels" $data | nindent 4 }} 15 | -------------------------------------------------------------------------------- /api/helm/api/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ include "api.fullname" . }} 5 | labels: 6 | {{- include "api.labels" . | nindent 4 }} 7 | type: Opaque 8 | data: 9 | {{- if .Values.postgresql.enabled }} 10 | {{- $postgresqlFullName := include "postgresql.fullname" . }} 11 | database-url: {{ printf "pgsql://%s:%s@%s-postgresql/%s?serverVersion=12" .Values.postgresql.postgresqlUsername .Values.postgresql.postgresqlPassword $postgresqlFullName .Values.postgresql.postgresqlDatabase | b64enc | quote }} 12 | {{- else }} 13 | database-url: {{ .Values.postgresql.url | b64enc | quote }} 14 | {{- end }} 15 | secret: {{ .Values.php.secret | default (randAlphaNum 40) | b64enc | quote }} 16 | mercure-jwt-token: {{ .Values.php.mercure.jwtToken | b64enc | quote }} 17 | -------------------------------------------------------------------------------- /api/helm/api/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "api.serviceAccountName" . }} 6 | labels: 7 | {{- include "api.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end -}} 13 | -------------------------------------------------------------------------------- /api/helm/api/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "api.fullname" . -}} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: $fullName-test-connection 6 | labels: 7 | {{- include "api.labels" . | nindent 4 }} 8 | annotations: 9 | "helm.sh/hook": test-success 10 | spec: 11 | containers: 12 | - name: wget 13 | image: busybox 14 | command: ['wget'] 15 | args: ['{{ if .Values.varnish.enabled }}{{ $fullName }}-varnish:{{ .Values.varnish.service.port }}{{ else }}{{ $fullName }}-nginx:{{ .Values.nginx.service.port }}{{ end }}'] 16 | restartPolicy: Never 17 | -------------------------------------------------------------------------------- /api/helm/api/templates/varnish-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.varnish.enabled -}} 2 | {{- $name := "varnish" -}} 3 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: {{ include "api.fullname" . }}-{{ $name }} 8 | labels: 9 | {{- include "api.labels" $data | nindent 4 }} 10 | spec: 11 | type: {{ .Values.varnish.service.type }} 12 | ports: 13 | - port: {{ .Values.varnish.service.port }} 14 | targetPort: http 15 | protocol: TCP 16 | name: http 17 | selector: 18 | {{- include "api.selectorLabels" $data | nindent 4 }} 19 | {{- end -}} 20 | -------------------------------------------------------------------------------- /api/hosts.txt: -------------------------------------------------------------------------------- 1 | https://google.com 2 | https://google.fr 3 | https://google.net 4 | https://google.uk 5 | https://google.be 6 | https://google.nl 7 | https://google.va 8 | https://google.mdr 9 | facebook 10 | toto.fr 11 | http://qzdjkb.d 12 | https://devcv.fr 13 | https://jenaye.fr 14 | -------------------------------------------------------------------------------- /api/migrations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/migrations/.gitignore -------------------------------------------------------------------------------- /api/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | tests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /api/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/public/favicon.ico -------------------------------------------------------------------------------- /api/public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /api/public/media/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/public/media/.gitignore -------------------------------------------------------------------------------- /api/src/Constraint/Ip.php: -------------------------------------------------------------------------------- 1 | validateCidr($value)) { 54 | return; 55 | } 56 | 57 | parent::validate($value, $constraint); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /api/src/Controller/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/src/Controller/.gitignore -------------------------------------------------------------------------------- /api/src/Controller/CreateMediaObjectAction.php: -------------------------------------------------------------------------------- 1 | files->get('file'); 14 | if (!$uploadedFile) { 15 | throw new BadRequestHttpException('"file" is required'); 16 | } 17 | 18 | $mediaObject = new MediaObject(); 19 | $mediaObject->file = $uploadedFile; 20 | 21 | return $mediaObject; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/src/Controller/MediaController.php: -------------------------------------------------------------------------------- 1 | downloadHandler = $downloadHandler; 25 | $this->manager = $manager; 26 | } 27 | 28 | public function __invoke(string $name) 29 | { 30 | $media = $this->manager->getRepository(MediaObject::class)->findOneByFilePath($name); 31 | 32 | return $this->downloadHandler->downloadObject($media, 'file', MediaObject::class, null, false); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api/src/Controller/UploadNmapController.php: -------------------------------------------------------------------------------- 1 | locale; 25 | } 26 | 27 | public function setLocale(?string $locale): void 28 | { 29 | $this->locale = $locale ?? 'en'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /api/src/Entity/Translation/VulnTranslation.php: -------------------------------------------------------------------------------- 1 | security = $security; 19 | } 20 | 21 | public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null) 22 | { 23 | $this->addWhere($queryBuilder, $resourceClass); 24 | } 25 | 26 | private function addWhere(QueryBuilder $queryBuilder, string $resourceClass) 27 | { 28 | if (Mission::class !== $resourceClass) { 29 | return; 30 | } 31 | 32 | if ($this->security->isGranted('ROLE_MANAGER')) { 33 | return; 34 | } 35 | 36 | $rootAlias = $queryBuilder->getRootAliases()[0]; 37 | $queryBuilder->innerJoin(\sprintf('%s.%s', $rootAlias, $this->security->getUser() instanceof User ? 'users' : 'clients'), 'u'); 38 | $queryBuilder->andWhere('u = :user'); 39 | $queryBuilder->setParameter('user', $this->security->getUser()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/Filter/MissionTypeFilter.php: -------------------------------------------------------------------------------- 1 | isPropertyEnabled($property, $resourceClass)) { 21 | return; 22 | } 23 | 24 | $parameterName = $queryNameGenerator->generateParameterName(self::FILTER_NAME); 25 | $queryBuilder->andWhere('SUBSTRING(o.missionType.name, 1, 2) in (:'.$parameterName.')'); 26 | $queryBuilder->setParameter($parameterName, $value); 27 | } 28 | 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function getDescription(string $resourceClass): array 34 | { 35 | if (!$this->properties) { 36 | return []; 37 | } 38 | 39 | return [ 40 | 'missionType' => [ 41 | 'property' => self::FILTER_NAME, 42 | 'type' => 'string', 43 | 'required' => false, 44 | 'strategy' => 'exact', 45 | 'swagger' => [ 46 | 'description' => '', 47 | 'name' => self::FILTER_NAME, 48 | 'type' => 'string', 49 | ], 50 | ], 51 | ]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /api/src/Listener/LocaleListener.php: -------------------------------------------------------------------------------- 1 | getRequest(); 14 | 15 | $language = $request->getPreferredLanguage(); 16 | 17 | $request->setLocale($language ?? 'en'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /api/src/Migrations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/src/Migrations/.gitignore -------------------------------------------------------------------------------- /api/src/Repository/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/src/Repository/.gitignore -------------------------------------------------------------------------------- /api/src/Repository/ClientRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('c') 29 | ->andWhere('c.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('c.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Client 41 | { 42 | return $this->createQueryBuilder('c') 43 | ->andWhere('c.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/HostRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('h') 29 | ->andWhere('h.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('h.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Host 41 | { 42 | return $this->createQueryBuilder('h') 43 | ->andWhere('h.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/HostVulnRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('h') 29 | ->andWhere('h.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('h.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?HostVuln 41 | { 42 | return $this->createQueryBuilder('h') 43 | ->andWhere('h.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/ImpactRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('i') 29 | ->andWhere('i.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('i.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Impact 41 | { 42 | return $this->createQueryBuilder('i') 43 | ->andWhere('i.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/MissionRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('m') 29 | ->andWhere('m.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('m.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Mission 41 | { 42 | return $this->createQueryBuilder('m') 43 | ->andWhere('m.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/NegativePointRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('n') 29 | ->andWhere('n.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('n.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?NegativePoint 41 | { 42 | return $this->createQueryBuilder('n') 43 | ->andWhere('n.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/NmapRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('n') 29 | ->andWhere('n.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('n.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Nmap 41 | { 42 | return $this->createQueryBuilder('n') 43 | ->andWhere('n.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/PositivePointRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('p') 29 | ->andWhere('p.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('p.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?PositivePoint 41 | { 42 | return $this->createQueryBuilder('p') 43 | ->andWhere('p.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/StepRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('s') 29 | ->andWhere('s.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('s.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Step 41 | { 42 | return $this->createQueryBuilder('s') 43 | ->andWhere('s.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/VulnRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('v') 29 | ->andWhere('v.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('v.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Vuln 41 | { 42 | return $this->createQueryBuilder('v') 43 | ->andWhere('v.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Serializer/MediaObjectNormalizer.php: -------------------------------------------------------------------------------- 1 | storage = $storage; 21 | } 22 | 23 | public function normalize($object, ?string $format = null, array $context = []) 24 | { 25 | $context[self::ALREADY_CALLED] = true; 26 | 27 | $object->contentUrl = $this->storage->resolveUri($object, 'file'); 28 | 29 | return $this->normalizer->normalize($object, $format, $context); 30 | } 31 | 32 | public function supportsNormalization($data, ?string $format = null, array $context = []): bool 33 | { 34 | if (isset($context[self::ALREADY_CALLED])) { 35 | return false; 36 | } 37 | 38 | return $data instanceof MediaObject; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /api/src/Serializer/TranslationContextBuilder.php: -------------------------------------------------------------------------------- 1 | decorated = $decorated; 19 | $this->authorizationChecker = $authorizationChecker; 20 | } 21 | 22 | public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array 23 | { 24 | $context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes); 25 | 26 | if (false === $normalization && isset($context['groups'])) { 27 | $context['groups'][] = 'admin_write'; 28 | } 29 | 30 | if (true === $normalization && isset($context['groups'])) { 31 | $context['groups'][] = 'admin_read'; 32 | $context['groups'][] = 'translations'; 33 | } 34 | 35 | return $context; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/Subscriber/JWTGenerationSubscriber.php: -------------------------------------------------------------------------------- 1 | roleHierarchy = $roleHierarchy; 18 | } 19 | 20 | public function onJWTCreated(JWTCreatedEvent $event): void 21 | { 22 | /** @var User $user */ 23 | $user = $event->getUser(); 24 | 25 | // Override data into JWT 26 | $payload = $event->getData(); 27 | $payload['exp'] = time() + (60 * 60 * 24 * 30 * 6); // six months token lifetime 28 | $payload['user'] = sprintf('/users/%s', $user->getId()); 29 | $payload['roles'] = $this->roleHierarchy->getReachableRoleNames($user->getRoles()); 30 | 31 | $event->setData($payload); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/src/Traits/DescriptionTrait.php: -------------------------------------------------------------------------------- 1 | description; 20 | } 21 | 22 | public function setDescription(?string $description): self 23 | { 24 | $this->description = $description; 25 | 26 | return $this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/src/Traits/IdTrait.php: -------------------------------------------------------------------------------- 1 | id; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /api/src/Traits/LocaleTrait.php: -------------------------------------------------------------------------------- 1 | locale; 9 | } 10 | 11 | public function setLocale(?string $locale): void 12 | { 13 | $this->locale = $locale; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /api/src/Traits/NameTrait.php: -------------------------------------------------------------------------------- 1 | name; 19 | } 20 | 21 | public function setName(string $name): self 22 | { 23 | $this->name = $name; 24 | 25 | return $this; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/src/Traits/RemediationTrait.php: -------------------------------------------------------------------------------- 1 | remediation; 19 | } 20 | 21 | public function setRemediation(?string $remediation): self 22 | { 23 | $this->remediation = $remediation; 24 | 25 | return $this; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/src/Traits/Translatable/TranslatableDescriptionTrait.php: -------------------------------------------------------------------------------- 1 | getTranslation()->getDescription(); 19 | } 20 | 21 | public function setDescription(string $description): self 22 | { 23 | $this->getTranslation()->setDescription($description); 24 | return $this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /api/src/Traits/Translatable/TranslatableNameTrait.php: -------------------------------------------------------------------------------- 1 | getTranslation()->getName(); 18 | } 19 | 20 | public function setName(string $name): self 21 | { 22 | $this->getTranslation()->setName($name); 23 | return $this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/src/Traits/Translatable/TranslatableRemediationTrait.php: -------------------------------------------------------------------------------- 1 | getTranslation()->getRemediation(); 18 | } 19 | 20 | public function setRemediation(string $remediation): self 21 | { 22 | $this->getTranslation()->setRemediation($remediation); 23 | return $this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/src/Traits/TypeTrait.php: -------------------------------------------------------------------------------- 1 | id; 29 | } 30 | 31 | public function getName(): ?string 32 | { 33 | return $this->name; 34 | } 35 | 36 | public function setName(string $name): self 37 | { 38 | $this->name = $name; 39 | 40 | return $this; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /api/src/Voter/MissionVoter.php: -------------------------------------------------------------------------------- 1 | security = $security; 22 | } 23 | 24 | protected function supports($attribute, $subject) 25 | { 26 | if (!\in_array($attribute, [self::ASSOCIATED_USER_ENABLED], true)) { 27 | return false; 28 | } 29 | 30 | if (!$subject instanceof Mission) { 31 | return false; 32 | } 33 | 34 | return true; 35 | } 36 | 37 | protected function voteOnAttribute($attribute, $subject, TokenInterface $token) 38 | { 39 | if ($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_MANAGER')) { 40 | return true; 41 | } 42 | 43 | if (!$this->security->isGranted('ROLE_MISSION_GET_ITEM')) { 44 | return false; 45 | } 46 | 47 | $user = $token->getUser(); 48 | if (!$user instanceof UserInterface) { 49 | return false; 50 | } 51 | 52 | /** @var Mission $mission */ 53 | $mission = $subject; 54 | 55 | return $mission->getUsers()->contains($user); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /api/templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | {% block body %}{% endblock %} 10 | {% block javascripts %}{% endblock %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /api/templates/upload_host/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block title %}Hello UploadHostController!{% endblock %} 4 | 5 | {% block body %} 6 | 10 | 11 |
12 |

Hello {{ controller_name }}! ✅

13 | 14 | This friendly message is coming from: 15 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /api/templates/upload_nmap/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block title %}Hello UploadNmapController!{% endblock %} 4 | 5 | {% block body %} 6 | 10 | 11 |
12 |

Hello {{ controller_name }}! ✅

13 | 14 | This friendly message is coming from: 15 | 19 |
20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /api/tests/Behat/ClientContext.php: -------------------------------------------------------------------------------- 1 | getContainer()->get('test.service_container'); 18 | $this->client = $testContainer->get('test.client'); 19 | } 20 | 21 | /** 22 | * @Given next client call will return a response with code :code 23 | */ 24 | public function clientWillReturnAResponseWithCode(int $code): void 25 | { 26 | $this->client->mockHandler->append(new Response($code, [])); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/tests/Behat/ClientTrait.php: -------------------------------------------------------------------------------- 1 | getDriver(); 16 | 17 | if (!$driver instanceof BrowserKitDriver) { 18 | throw new \RuntimeException(\sprintf('Driver should be an instance of BrowserKitDriver, got "%s"', \get_class($driver))); 19 | } 20 | 21 | return $driver->getClient(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /api/tests/Behat/DemoContext.php: -------------------------------------------------------------------------------- 1 | kernel = $kernel; 29 | } 30 | 31 | /** 32 | * @When a demo scenario sends a request to :path 33 | */ 34 | public function aDemoScenarioSendsARequestTo(string $path): void 35 | { 36 | $this->response = $this->kernel->handle(Request::create($path, 'GET')); 37 | } 38 | 39 | /** 40 | * @Then the response should be received 41 | */ 42 | public function theResponseShouldBeReceived(): void 43 | { 44 | if ($this->response === null) { 45 | throw new \RuntimeException('No response received'); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /api/tests/Behat/FeatureContext.php: -------------------------------------------------------------------------------- 1 | iCreateAResource($data->getRaw()); 20 | } 21 | 22 | /** 23 | * @When I try to update a host on id::arg1 with: 24 | */ 25 | public function iTryToUpdateAnHostWith(PyStringNode $data, $id) 26 | { 27 | $this->iUpdateAResource($data->getRaw(), $id); 28 | } 29 | 30 | /** 31 | * @When I try to delete a host with id: :arg1 32 | */ 33 | public function iTryToDeleteAnHostWithId($id) 34 | { 35 | $this->iDeleteAResource($id); 36 | } 37 | 38 | /** 39 | * @Then the response should contain :arg1 40 | */ 41 | public function theResponseShouldContain($arg1) 42 | { 43 | $this->assertSession()->responseContains($arg1); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /api/tests/Behat/ImpactContext.php: -------------------------------------------------------------------------------- 1 | iCreateAResource($data->getRaw()); 20 | } 21 | 22 | /** 23 | * @When I try to update an impact on id::arg1 with: 24 | */ 25 | public function iTryToUpdateAnImpactWith(PyStringNode $data, $id) 26 | { 27 | $this->iUpdateAResource($data->getRaw(), $id); 28 | } 29 | 30 | /** 31 | * @When I try to delete an impact with id: :arg1 32 | */ 33 | public function iTryToDeleteAnImpactWithId($id) 34 | { 35 | $this->iDeleteAResource($id); 36 | } 37 | 38 | /** 39 | * @Then the response should contain :arg1 40 | */ 41 | public function theResponseShouldContain($arg1) 42 | { 43 | $this->assertSession()->responseContains($arg1); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /api/tests/Behat/StepContext.php: -------------------------------------------------------------------------------- 1 | iCreateAResource($data->getRaw()); 20 | } 21 | 22 | /** 23 | * @When I try to update a step on id::arg1 with: 24 | */ 25 | public function iTryToUpdateAnStepWith(PyStringNode $data, $id) 26 | { 27 | $this->iUpdateAResource($data->getRaw(), $id); 28 | } 29 | 30 | /** 31 | * @When I try to delete a step with id: :arg1 32 | */ 33 | public function iTryToDeleteAnStepWithId($id) 34 | { 35 | $this->iDeleteAResource($id); 36 | } 37 | 38 | /** 39 | * @Then the response should contain :arg1 40 | */ 41 | public function theResponseShouldContain($arg1) 42 | { 43 | $this->assertSession()->responseContains($arg1); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /api/tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__).'/.env'); 11 | } 12 | -------------------------------------------------------------------------------- /api/translations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/api/translations/.gitignore -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | -------------------------------------------------------------------------------- /client/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "jasmine": true, 5 | "jest": true, 6 | "es6": true, 7 | "node": true 8 | }, 9 | "extends": [ 10 | "eslint:recommended", 11 | "prettier/@typescript-eslint", 12 | "plugin:@typescript-eslint/recommended", 13 | "plugin:prettier/recommended", 14 | "plugin:security/recommended" 15 | 16 | ], 17 | "parserOptions": { 18 | "ecmaVersion": 8, 19 | "sourceType": "module", 20 | "ecmaFeatures": { 21 | "jsx": true 22 | } 23 | }, 24 | "parser": "@typescript-eslint/parser", 25 | "plugins": ["angular", "@typescript-eslint", "prettier", "import", "security"], 26 | "rules": { 27 | "import/first": "error", 28 | "no-console": "warn", 29 | "no-eval": "error", 30 | "prettier/prettier": ["error", { "singleQuote": true }], 31 | "react/prop-types": 0 32 | }, 33 | "settings": { 34 | "import/resolver": { 35 | "node": { 36 | "paths": ["src"] 37 | } 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.16.1-alpine AS runner 2 | 3 | WORKDIR /usr/src/app 4 | 5 | ARG API_BASE_URL 6 | ARG TRANSPORT 7 | ENV API_BASE_URL $API_BASE_URL 8 | ENV TRANSPORT $TRANSPORT 9 | RUN echo "Api base url: ${TRANSPORT}${API_BASE_URL}" 10 | 11 | COPY package.json ./ 12 | RUN npm install 13 | 14 | COPY . . 15 | 16 | RUN echo -e "\ 17 | export const environment = { \n\ 18 | production: true, \n\ 19 | API_DOMAIN: '${API_BASE_URL}', \n\ 20 | TRANSPORT: '${TRANSPORT}', \n\ 21 | API_ENDPOINT: '/api', \n\ 22 | MAPS_KEY: '', \n\ 23 | version: '1.2.0', \n\ 24 | environment: 'prod', \n\ 25 | } \n\ 26 | " > src/environments/environment.prod.ts 27 | RUN echo -e "\ 28 | export const environment = { \n\ 29 | production: true, \n\ 30 | API_DOMAIN: '${API_BASE_URL}', \n\ 31 | TRANSPORT: '${TRANSPORT}', \n\ 32 | API_ENDPOINT: '/api', \n\ 33 | MAPS_KEY: '', \n\ 34 | version: '1.2.0', \n\ 35 | environment: 'prod', \n\ 36 | } \n\ 37 | " > src/environments/environment.ts 38 | 39 | FROM node:12.16.1-alpine AS builder 40 | WORKDIR /usr/src/app 41 | COPY --from=runner /usr/src/app/ . 42 | RUN npm install 43 | RUN npm run build --configuration=prod 44 | 45 | FROM nginx:1.15.8-alpine 46 | 47 | COPY --from=builder /usr/src/app/dist/front/ /usr/share/nginx/html 48 | COPY --from=builder /usr/src/app/docker/nginx.conf /etc/nginx/conf.d/default.conf 49 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Front 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.2.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | 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`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /client/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "https://smersh.lan", 3 | "viewportHeight": 760, 4 | "viewportWidth": 1080 5 | } 6 | -------------------------------------------------------------------------------- /client/cypress/.gitignore: -------------------------------------------------------------------------------- 1 | videos/ 2 | screenshots/ 3 | -------------------------------------------------------------------------------- /client/cypress/fixtures/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "admin": { 4 | "username": "admin", 5 | "password": "admin" 6 | }, 7 | "manager": { 8 | "username": "manager", 9 | "password": "manager" 10 | }, 11 | "guest": { 12 | "username": "guest", 13 | "password": "guest" 14 | } 15 | }, 16 | "routes": { 17 | "client": { 18 | "login": "/login", 19 | "dashboard": "/", 20 | "resources": { 21 | "missions": { 22 | "base": "/missions", 23 | "list": "/missions/all" 24 | }, 25 | "vulnerabilities": "/vulnerabilities" 26 | } 27 | }, 28 | "api": { 29 | "base": "https://api.smersh.lan", 30 | "api": "/api", 31 | "login": "/authentication_token", 32 | "missions": "/missions", 33 | "vulnerabilities": "/vulns" 34 | } 35 | }, 36 | "storage": { 37 | "token": "token" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/cypress/integration/dashboard.spec.js: -------------------------------------------------------------------------------- 1 | describe('Should test dashboard', () => { 2 | beforeEach(function () { 3 | cy.loadData(); 4 | }); 5 | 6 | it('Should not be redirect to login if I am not authenticated', function () { 7 | cy.visit(`${this.data.routes.client.dashboard}`); 8 | cy.url().should('include', `${this.data.routes.client.login}`); 9 | }); 10 | 11 | it('Should be redirect if I am authenticated', function () { 12 | const { 13 | routes: { 14 | client: { 15 | dashboard, 16 | resources: { 17 | missions: { base }, 18 | }, 19 | }, 20 | }, 21 | } = this.data; 22 | cy.loginAs('admin'); 23 | cy.visit(dashboard); 24 | cy.url().should('include', dashboard); 25 | cy.url().should('include', base); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /client/cypress/integration/helpers.js: -------------------------------------------------------------------------------- 1 | export const requestBaseApi = ({ base }, endpoint) => `${base}${endpoint}`; 2 | 3 | export const requestApi = (api, endpoint) => 4 | requestBaseApi(api, `${api.api}${endpoint}`); 5 | -------------------------------------------------------------------------------- /client/cypress/integration/login.spec.js: -------------------------------------------------------------------------------- 1 | describe('Admin login', () => { 2 | beforeEach(function () { 3 | cy.loadData(); 4 | }); 5 | 6 | it('Should login successfully as admin', function () { 7 | cy.loginAs('admin'); 8 | }); 9 | 10 | it('Should login successfully as manager', function () { 11 | cy.loginAs('manager'); 12 | }); 13 | 14 | it('Should login successfully as client', function () { 15 | cy.loginAs('client'); 16 | }); 17 | 18 | it('Should not login as nonexistent user', function () { 19 | cy.loginAs('', { 20 | isValid: false, 21 | password: 'invalid', 22 | username: 'nonexistent', 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /client/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | } 23 | -------------------------------------------------------------------------------- /client/cypress/support/commands/generic/form.js: -------------------------------------------------------------------------------- 1 | Cypress.Commands.add('fillInput', (input, value) => { 2 | cy.get(`[data-cy=${input}]`).type(value); 3 | }); 4 | 5 | Cypress.Commands.add('selectValueIn', (input) => { 6 | cy.get(`input[name=${input}]`).click(); 7 | cy.get(`[data-cy=${input}-item]`).last().click(); 8 | }); 9 | -------------------------------------------------------------------------------- /client/cypress/support/commands/generic/index.js: -------------------------------------------------------------------------------- 1 | import './list'; 2 | import './form'; 3 | -------------------------------------------------------------------------------- /client/cypress/support/commands/generic/list.js: -------------------------------------------------------------------------------- 1 | import { requestApi } from '../../../integration/helpers'; 2 | 3 | Cypress.Commands.add( 4 | 'waitForListRequest', 5 | function (resource, actions, callback = () => undefined, empty = false) { 6 | const { 7 | routes: { 8 | api, 9 | client: { resources }, 10 | }, 11 | } = this.data; 12 | cy.waitForRequest( 13 | { 14 | method: 'GET', 15 | url: requestApi(api, api[resource]), 16 | }, 17 | () => { 18 | cy.visit(resources[resource].list); 19 | }, 20 | () => { 21 | (actions ?? ['create', 'delete', 'edit', 'show']).forEach((action) => { 22 | expect( 23 | empty 24 | ? cy.get(`[data-cy=action-button-${action}]`).should('not.exist') 25 | : cy 26 | .get(`[data-cy=action-button-${action}]`) 27 | .should('have.not.length', 0) 28 | ); 29 | }); 30 | callback(); 31 | } 32 | ); 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /client/cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | import './commands/generic'; 19 | 20 | // Alternatively you can use CommonJS syntax: 21 | // require('./commands') 22 | -------------------------------------------------------------------------------- /client/docker/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | location / { 4 | root /usr/share/nginx/html; 5 | index index.html index.htm; 6 | try_files $uri $uri/ /index.html =404; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ 31 | spec: { 32 | displayStacktrace: StacktraceOption.PRETTY 33 | } 34 | })); 35 | } 36 | }; -------------------------------------------------------------------------------- /client/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('front app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /client/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/e2e/tsconfig.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/e2e", 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/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-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/front'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /client/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/app/app.component.scss -------------------------------------------------------------------------------- /client/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostBinding, OnInit } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Router } from '@angular/router'; 4 | import { DecodedToken, Token } from 'src/app/storage/Token'; 5 | import { MatDialog } from '@angular/material/dialog'; 6 | import { Theme, ThemeService } from 'src/app/services/theme.service'; 7 | 8 | @Component({ 9 | selector: 'app-root', 10 | templateUrl: './app.component.html', 11 | styleUrls: ['./app.component.scss'], 12 | }) 13 | export class AppComponent implements OnInit { 14 | title = 'Smersh'; 15 | @HostBinding('class') className = Theme.LIGHT_THEME; 16 | protected logged: boolean; 17 | 18 | constructor( 19 | private http: HttpClient, 20 | private router: Router, 21 | private dialog: MatDialog, 22 | private themeService: ThemeService 23 | ) {} 24 | 25 | ngOnInit(): void { 26 | this.themeService.onChangeTheme.subscribe( 27 | (theme) => (this.className = theme) 28 | ); 29 | 30 | // check if valid jwt 31 | if (Date.now() < new DecodedToken().getDecoded().exp * 1000) { 32 | this.logged = true; 33 | } else { 34 | new Token().reset(); 35 | this.router.navigateByUrl('/login'); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/src/app/components/add-vulns-to-host-external/add-vulns-to-host-external.component.scss: -------------------------------------------------------------------------------- 1 | .smersh-full-width { 2 | width: 100%; 3 | } 4 | 5 | .addSpace { 6 | padding-right: 20px; 7 | } 8 | 9 | .smersh-form { 10 | min-width: 150px; 11 | max-width: 500px; 12 | width: 100%; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/app/components/clients/clientsCreate.component.ts: -------------------------------------------------------------------------------- 1 | import { GenericCreateComponent } from 'src/app/components/generic'; 2 | import { ClientsService } from 'src/app/services/clients.service'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { Email, FirstName, Input, LastName, Name, Phone, } from 'src/app/form/Input'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { ClientRouter } from 'src/app/router/ClientRouter'; 7 | import { Component } from '@angular/core'; 8 | 9 | @Component({ 10 | selector: 'app-clients-create', 11 | templateUrl: '../generic/form/generic-form.component.html', 12 | styleUrls: [], 13 | }) 14 | export class ClientsCreateComponent extends GenericCreateComponent { 15 | public singularResource = 'client'; 16 | public routerHelper = ClientRouter; 17 | public inputs: Input[] = [ 18 | new Name(), 19 | new Email(), 20 | new FirstName(), 21 | new LastName(), 22 | new Phone(), 23 | ]; 24 | 25 | constructor( 26 | protected service: ClientsService, 27 | protected router: Router, 28 | protected route: ActivatedRoute, 29 | protected snackBar: MatSnackBar 30 | ) { 31 | super(service, router, route, snackBar); 32 | } 33 | 34 | ngOnInit(): void { 35 | super.ngOnInit(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/app/components/clients/clientsEdit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericEditComponent } from 'src/app/components/generic'; 3 | import { ClientsService } from 'src/app/services/clients.service'; 4 | import { ActivatedRoute, Router } from '@angular/router'; 5 | import { Email, FirstName, Input, LastName, Name, Phone, } from 'src/app/form/Input'; 6 | import { MatSnackBar } from '@angular/material/snack-bar'; 7 | import { ClientRouter } from 'src/app/router/ClientRouter'; 8 | 9 | @Component({ 10 | selector: 'app-clients-edit', 11 | templateUrl: '../generic/form/generic-form.component.html', 12 | styleUrls: [], 13 | }) 14 | export class ClientsEditComponent extends GenericEditComponent { 15 | public singularResource = 'client'; 16 | public routerHelper = ClientRouter; 17 | public inputs: Input[] = [ 18 | new Name(), 19 | new Email(), 20 | new FirstName(), 21 | new LastName(), 22 | new Phone(), 23 | ]; 24 | 25 | constructor( 26 | protected service: ClientsService, 27 | protected router: Router, 28 | protected route: ActivatedRoute, 29 | protected snackBar: MatSnackBar 30 | ) { 31 | super(service, router, route, snackBar); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/src/app/components/clients/clientsList.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { ClientsService } from 'src/app/services/clients.service'; 4 | import { GenericListComponent, SHOW } from 'src/app/components/generic'; 5 | import { ClientRouter } from 'src/app/router/ClientRouter'; 6 | 7 | @Component({ 8 | selector: 'app-clients-list', 9 | templateUrl: '../generic/list/generic-list.component.html', 10 | styleUrls: [], 11 | }) 12 | export class ClientsListComponent extends GenericListComponent { 13 | resource = 'clients'; 14 | singularResource = 'Client'; 15 | routerHelper = ClientRouter; 16 | public filters = ['name', 'mail', 'phone']; 17 | protected excludedFields = ['@id', '@type', SHOW.name]; 18 | 19 | constructor(protected service: ClientsService, protected router: Router) { 20 | super(service, router); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/app/components/clients/index.ts: -------------------------------------------------------------------------------- 1 | import { ClientsCreateComponent } from './clientsCreate.component'; 2 | import { ClientsEditComponent } from './clientsEdit.component'; 3 | import { ClientsListComponent } from './clientsList.component'; 4 | 5 | export { ClientsCreateComponent, ClientsEditComponent, ClientsListComponent }; 6 | -------------------------------------------------------------------------------- /client/src/app/components/conclusion/conclusion.component.html: -------------------------------------------------------------------------------- 1 |

Positive points :

2 |
3 | {{ positive.name }} 4 |
5 | 6 | 7 |

Negative points :

8 |
9 | {{ negative.name }} 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /client/src/app/components/conclusion/conclusion.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/app/components/conclusion/conclusion.component.scss -------------------------------------------------------------------------------- /client/src/app/components/conclusion/conclusion.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { NegativePointsService } from 'src/app/services/negative.service'; 3 | import { PositivePointsService } from 'src/app/services/positive.service'; 4 | 5 | @Component({ 6 | selector: 'app-conclusion', 7 | templateUrl: './conclusion.component.html', 8 | styleUrls: ['./conclusion.component.scss'], 9 | }) 10 | export class ConclusionComponent implements OnInit { 11 | public positivePoints = []; 12 | public negativePoints = []; 13 | 14 | constructor( 15 | private negativePointsService: NegativePointsService, 16 | private positivePointsServices: PositivePointsService 17 | ) {} 18 | 19 | ngOnInit(): void { 20 | Promise.all( 21 | [this.positivePointsServices, this.negativePointsService].map((action) => 22 | action.getData() 23 | ) 24 | ).then(([{ data: positive }, { data: negative }]) => { 25 | this.positivePoints = positive; 26 | this.negativePoints = negative; 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/app/components/edit-vuln-with-state/edit-vuln-with-state.component.scss: -------------------------------------------------------------------------------- 1 | .example-form { 2 | min-width: 150px; 3 | max-width: 500px; 4 | width: 100%; 5 | } 6 | 7 | .example-full-width { 8 | width: 100%; 9 | } 10 | 11 | .mat-form-field + .mat-form-field { 12 | margin-left: 3rem; 13 | } 14 | 15 | .bold { 16 | font-weight: bold; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/app/components/errors/errors.component.html: -------------------------------------------------------------------------------- 1 |

errors works!

2 | -------------------------------------------------------------------------------- /client/src/app/components/errors/errors.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/app/components/errors/errors.component.scss -------------------------------------------------------------------------------- /client/src/app/components/errors/errors.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-errors', 5 | templateUrl: './errors.component.html', 6 | styleUrls: ['./errors.component.scss'], 7 | }) 8 | export class ErrorsComponent implements OnInit { 9 | constructor() { 10 | } 11 | 12 | ngOnInit(): void { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/components/generic/form/generic-create.component.ts: -------------------------------------------------------------------------------- 1 | import { GenericFormComponent } from './generic-form.component'; 2 | import { NgForm } from '@angular/forms'; 3 | 4 | export abstract class GenericCreateComponent extends GenericFormComponent { 5 | formType = 'Create'; 6 | 7 | onSubmit({ value }: NgForm): void { 8 | this.service 9 | .insert({ ...this.itemTransformer(), ...value }) 10 | .then(() => { 11 | this.notifyActionSuccessAndRedirect('created'); 12 | }) 13 | .catch(({ error: { ['hydra:description']: error }, status }) => { 14 | if (status === '400') { 15 | this.openSnackBar(`Error : ${error}`); 16 | } 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/src/app/components/generic/form/generic-edit.component.ts: -------------------------------------------------------------------------------- 1 | import { GenericFormComponent } from './generic-form.component'; 2 | import { NgForm } from '@angular/forms'; 3 | 4 | export abstract class GenericEditComponent extends GenericFormComponent { 5 | formType = 'Edit'; 6 | id: string; 7 | 8 | fetchItem(): Promise { 9 | return this.service.getDataById(this.id).then((item) => { 10 | this.item = item; 11 | }); 12 | } 13 | 14 | initialize(): void { 15 | this.route.params.subscribe(({ id }) => { 16 | this.id = id; 17 | this.fetchItem().then(); 18 | }); 19 | } 20 | 21 | onSubmit({ value }: NgForm): void { 22 | this.service 23 | .update(this.id, { 24 | ...this.itemTransformer(), 25 | ...value, 26 | }) 27 | .then(() => { 28 | this.notifyActionSuccessAndRedirect('updated'); 29 | }) 30 | .catch(({ error: { ['hydra:description']: error }, status }) => { 31 | if (status === '400') { 32 | this.openSnackBar(`Error : ${error}`); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/app/components/generic/form/queryable-input.component.html: -------------------------------------------------------------------------------- 1 | 2 | Select {{ name }} 3 | 4 | 5 | 10 | {{ selectedRecord.label }} 11 | cancel 12 | 13 | 21 | 22 | 23 | 24 | 30 | 31 | 33 | 34 | {{ option.label }} 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /client/src/app/components/generic/index.ts: -------------------------------------------------------------------------------- 1 | import { GenericCreateComponent } from './form/generic-create.component'; 2 | import { GenericEditComponent } from './form/generic-edit.component'; 3 | import { GenericFormComponent } from './form/generic-form.component'; 4 | import { CREATE, DELETE, EDIT, GenericListComponent, SHOW, } from './list/generic-list.component'; 5 | 6 | export { 7 | CREATE, 8 | DELETE, 9 | EDIT, 10 | GenericCreateComponent, 11 | GenericEditComponent, 12 | GenericFormComponent, 13 | GenericListComponent, 14 | SHOW, 15 | }; 16 | -------------------------------------------------------------------------------- /client/src/app/components/homepage/homepage.component.html: -------------------------------------------------------------------------------- 1 |
2 |

homepage works!

3 | 4 | Graph with counts of missions etc ? 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /client/src/app/components/homepage/homepage.component.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/app/components/homepage/homepage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-homepage', 6 | templateUrl: './homepage.component.html', 7 | styleUrls: ['./homepage.component.scss'], 8 | }) 9 | export class HomepageComponent implements OnInit { 10 | constructor(private router: Router) {} 11 | 12 | ngOnInit(): void { 13 | this.router.navigateByUrl('/missions'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/app/components/hosts/hostsEdit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericEditComponent } from 'src/app/components/generic'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { Input, Name, TextInput } from 'src/app/form/Input'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { HostRouter } from 'src/app/router/HostRouter'; 7 | import { HostsService } from 'src/app/services/hosts.service'; 8 | 9 | @Component({ 10 | selector: 'app-hosts-edit', 11 | templateUrl: '../generic/form/generic-form.component.html', 12 | styleUrls: [], 13 | }) 14 | export class HostsEditComponent extends GenericEditComponent { 15 | public singularResource = 'host'; 16 | public routerHelper = HostRouter; 17 | public inputs: Input[] = [ 18 | new Name(), 19 | new TextInput({ 20 | name: 'technology', 21 | label: 'Technology' 22 | }), 23 | ]; 24 | 25 | constructor( 26 | protected service: HostsService, 27 | protected router: Router, 28 | protected route: ActivatedRoute, 29 | protected snackBar: MatSnackBar 30 | ) { 31 | super(service, router, route, snackBar); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /client/src/app/components/hosts/hostsList.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { CREATE, DELETE, EDIT, GenericListComponent, SHOW, } from 'src/app/components/generic'; 4 | import { HostRouter } from 'src/app/router/HostRouter'; 5 | import { HostsService } from 'src/app/services/hosts.service'; 6 | 7 | @Component({ 8 | selector: 'app-hosts-list', 9 | templateUrl: '../generic/list/generic-list.component.html', 10 | styleUrls: [], 11 | }) 12 | export class HostsListComponent extends GenericListComponent { 13 | public filters = ['name', 'technology']; 14 | public buttonActions = [SHOW, EDIT, DELETE]; 15 | public actions = [...this.buttonActions]; 16 | resource = 'hosts'; 17 | singularResource = 'Host'; 18 | routerHelper = HostRouter; 19 | fields = ['id', 'name', 'technology', 'edit', 'delete']; 20 | protected excludedFields = [CREATE.name]; 21 | 22 | constructor(protected service: HostsService, protected router: Router) { 23 | super(service, router); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/app/components/hosts/index.ts: -------------------------------------------------------------------------------- 1 | import { HostsListComponent } from './hostsList.component'; 2 | import { HostsEditComponent } from './hostsEdit.component'; 3 | 4 | export { HostsEditComponent, HostsListComponent }; 5 | -------------------------------------------------------------------------------- /client/src/app/components/impacts/impactsCreate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericCreateComponent } from 'src/app/components/generic'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { Input, Name } from 'src/app/form/Input'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { ImpactRouter } from 'src/app/router/ImpactRouter'; 7 | import { ImpactsService } from 'src/app/services/impacts.service'; 8 | 9 | @Component({ 10 | selector: 'app-impacts-create', 11 | templateUrl: '../generic/form/generic-form.component.html', 12 | styleUrls: [], 13 | }) 14 | export class ImpactsCreateComponent extends GenericCreateComponent { 15 | public singularResource = 'impact'; 16 | public routerHelper = ImpactRouter; 17 | public inputs: Input[] = [new Name()]; 18 | 19 | constructor( 20 | protected service: ImpactsService, 21 | protected router: Router, 22 | protected route: ActivatedRoute, 23 | protected snackBar: MatSnackBar 24 | ) { 25 | super(service, router, route, snackBar); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/app/components/impacts/impactsEdit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericEditComponent } from 'src/app/components/generic'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { Input, Name } from 'src/app/form/Input'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { ImpactRouter } from 'src/app/router/ImpactRouter'; 7 | import { ImpactsService } from 'src/app/services/impacts.service'; 8 | 9 | @Component({ 10 | selector: 'app-impacts-edit', 11 | templateUrl: '../generic/form/generic-form.component.html', 12 | styleUrls: [], 13 | }) 14 | export class ImpactsEditComponent extends GenericEditComponent { 15 | public singularResource = 'impact'; 16 | public routerHelper = ImpactRouter; 17 | public inputs: Input[] = [new Name()]; 18 | 19 | constructor( 20 | protected service: ImpactsService, 21 | protected router: Router, 22 | protected route: ActivatedRoute, 23 | protected snackBar: MatSnackBar 24 | ) { 25 | super(service, router, route, snackBar); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/app/components/impacts/impactsList.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { GenericListComponent, SHOW } from 'src/app/components/generic'; 4 | import { ImpactsService } from 'src/app/services/impacts.service'; 5 | import { ImpactRouter } from 'src/app/router/ImpactRouter'; 6 | 7 | @Component({ 8 | selector: 'app-impacts-list', 9 | templateUrl: '../generic/list/generic-list.component.html', 10 | styleUrls: [], 11 | }) 12 | export class ImpactsListComponent extends GenericListComponent { 13 | resource = 'impacts'; 14 | singularResource = 'Impact'; 15 | routerHelper = ImpactRouter; 16 | protected excludedFields = ['@id', '@type', SHOW.name]; 17 | 18 | constructor(protected service: ImpactsService, protected router: Router) { 19 | super(service, router); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/app/components/impacts/index.ts: -------------------------------------------------------------------------------- 1 | import { ImpactsCreateComponent } from './impactsCreate.component'; 2 | import { ImpactsEditComponent } from './impactsEdit.component'; 3 | import { ImpactsListComponent } from './impactsList.component'; 4 | 5 | export { ImpactsCreateComponent, ImpactsEditComponent, ImpactsListComponent }; 6 | -------------------------------------------------------------------------------- /client/src/app/components/login/login.component.html: -------------------------------------------------------------------------------- 1 | 38 | -------------------------------------------------------------------------------- /client/src/app/components/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewEncapsulation } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Token } from 'src/app/storage/Token'; 4 | import { ConnectionService } from 'src/app/services/connection.service'; 5 | import { DashboardRouter } from 'src/app/router/DashboardRouter'; 6 | 7 | @Component({ 8 | selector: 'app-login', 9 | templateUrl: './login.component.html', 10 | styleUrls: ['./login.component.scss'], 11 | encapsulation: ViewEncapsulation.None, 12 | }) 13 | export class LoginComponent implements OnInit { 14 | public username: string; 15 | public password: string; 16 | public hide = true; 17 | 18 | constructor( 19 | private connectionService: ConnectionService, 20 | private router: Router 21 | ) { 22 | } 23 | 24 | ngOnInit(): void { 25 | return; 26 | } 27 | 28 | submit(): void { 29 | this.connectionService 30 | .login({ 31 | username: this.username, 32 | password: this.password 33 | }) 34 | .then(({token}: any) => { 35 | if (token) { 36 | new Token().set(token); 37 | this.router.navigateByUrl(DashboardRouter.redirectToList()); 38 | } 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /client/src/app/components/mission-my/mission-my.component.scss: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex !important; 3 | flex-direction: row; 4 | align-items: center; 5 | } 6 | 7 | .responsive_row { 8 | display: flex !important; 9 | flex-direction: row; 10 | align-items: center; 11 | justify-content: space-between; 12 | flex-wrap: wrap; 13 | } 14 | 15 | .clickable:hover { 16 | cursor: pointer; 17 | } 18 | 19 | .progress-bar { 20 | width: 10em; 21 | margin-left: 1em; 22 | } 23 | 24 | .progress-text { 25 | width: 2em; 26 | padding: 1em; 27 | } 28 | -------------------------------------------------------------------------------- /client/src/app/components/mission-my/mission-my.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { UsersService } from '../../services/users.service'; 3 | import { Router } from '@angular/router'; 4 | import { MissionRouter } from 'src/app/router/MissionRouter'; 5 | import { UserModelApplication } from 'src/app/model/User'; 6 | import { HostFromAPIInterface } from 'src/app/model/Host'; 7 | import { DecodedToken } from 'src/app/storage/Token'; 8 | 9 | @Component({ 10 | selector: 'app-mission-my', 11 | templateUrl: './mission-my.component.html', 12 | styleUrls: ['./mission-my.component.scss'], 13 | }) 14 | export class MissionMyComponent implements OnInit { 15 | public missions = []; 16 | public roles = []; 17 | 18 | constructor(private usersServices: UsersService, private router: Router) {} 19 | 20 | ngOnInit(): void { 21 | this.loadMissions(); 22 | } 23 | 24 | loadMissions(): void { 25 | const decodedToken = new DecodedToken().getDecoded(); 26 | this.roles = decodedToken.roles; 27 | this.usersServices 28 | .getDataById(decodedToken.user.split('/').pop()) 29 | .then(({ missions }: UserModelApplication) => { 30 | this.missions = missions.map(({ id, hosts, name }) => ({ 31 | name, 32 | current: 33 | ((hosts as HostFromAPIInterface[]).filter(({ checked }) => checked) 34 | .length / 35 | hosts.length) * 36 | 100, 37 | id, 38 | })); 39 | }); 40 | } 41 | 42 | editMission(id): void { 43 | this.router.navigateByUrl(MissionRouter.redirectToEdit(id)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/src/app/components/missions/index.ts: -------------------------------------------------------------------------------- 1 | import { MissionsCreateComponent } from './missionsCreate.component'; 2 | import { MissionsEditComponent } from './missionsEdit.component'; 3 | import { MissionsListComponent } from './missionsList.component'; 4 | 5 | export { 6 | MissionsCreateComponent, 7 | MissionsEditComponent, 8 | MissionsListComponent, 9 | }; 10 | -------------------------------------------------------------------------------- /client/src/app/components/missions/missionsList.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { GenericListComponent } from 'src/app/components/generic'; 4 | import { MissionsService } from 'src/app/services/missions.service'; 5 | import { MissionRouter } from 'src/app/router/MissionRouter'; 6 | 7 | @Component({ 8 | selector: 'app-missions-list', 9 | templateUrl: '../generic/list/generic-list.component.html', 10 | styleUrls: [], 11 | }) 12 | export class MissionsListComponent extends GenericListComponent { 13 | resource = 'missions'; 14 | singularResource = 'Mission'; 15 | routerHelper = MissionRouter; 16 | protected excludedFields = [ 17 | '@id', 18 | '@type', 19 | 'nessus', 20 | 'nmap', 21 | 'nessusFiler', 22 | 'nmapFiler', 23 | 'missionType', 24 | 'startDate', 25 | 'EndDate', 26 | ]; 27 | 28 | constructor(protected service: MissionsService, protected router: Router) { 29 | super(service, router); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/app/components/popup/popup.component.html: -------------------------------------------------------------------------------- 1 |

Delete Host {{ data.name }}

2 | 3 |

Are you sure ?

4 |
5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/src/app/components/popup/popup.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/app/components/popup/popup.component.scss -------------------------------------------------------------------------------- /client/src/app/components/popup/popup.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from '@angular/core'; 2 | import { MAT_DIALOG_DATA } from '@angular/material/dialog'; 3 | import { HostsService } from 'src/app/services/hosts.service'; 4 | import { MatSnackBar } from '@angular/material/snack-bar'; 5 | import { Router } from '@angular/router'; 6 | 7 | @Component({ 8 | selector: 'app-popup', 9 | templateUrl: './popup.component.html', 10 | styleUrls: ['./popup.component.scss'] 11 | }) 12 | export class PopupComponent implements OnInit { 13 | 14 | public durationInSeconds = 4; 15 | public missionId: string; 16 | 17 | constructor( 18 | @Inject(MAT_DIALOG_DATA) public data: any, 19 | private hostsService: HostsService, 20 | private _snackBar: MatSnackBar, 21 | private route: Router, 22 | ) { 23 | } 24 | 25 | ngOnInit(): void { 26 | this.missionId = this.route.url.split('/')[2]; 27 | } 28 | 29 | openSnackBar(message: string): void { 30 | this._snackBar.open(message, '', { 31 | duration: this.durationInSeconds * 1000, 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/src/app/components/side-bar/side-bar.component.scss: -------------------------------------------------------------------------------- 1 | .app-container { 2 | min-height: 100vh; 3 | } 4 | 5 | .example-container { 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | 10 | .example-is-mobile, .example-toolbar { 11 | position: -webkit-sticky; 12 | position: sticky; 13 | top: 0; 14 | z-index: 999; 15 | } 16 | 17 | h1.example-app-name { 18 | margin-left: 8px; 19 | } 20 | 21 | .example-sidenav-container { 22 | /* When the sidenav is not fixed, stretch the sidenav container to fill the available space. This 23 | causes `` to act as our scrolling element for desktop layouts. */ 24 | flex: 1; 25 | } 26 | 27 | .example-is-mobile .example-sidenav-container { 28 | /* When the sidenav is fixed, don't constrain the height of the sidenav container. This allows the 29 | `` to be our scrolling element for mobile layouts. */ 30 | flex: 1 0 auto; 31 | } 32 | 33 | .logo-navbar { 34 | width: 3%; 35 | padding-right: 10px; 36 | } 37 | 38 | 39 | .example-spacer { 40 | flex: 1 1 auto; 41 | } 42 | 43 | .smersh-button-row button { 44 | margin-right: 8px; 45 | } 46 | 47 | .sizeofNavbar { 48 | width: 15rem; 49 | } 50 | -------------------------------------------------------------------------------- /client/src/app/components/users/index.ts: -------------------------------------------------------------------------------- 1 | import { UsersCreateComponent } from './usersCreate.component'; 2 | import { UsersEditComponent } from './usersEdit.component'; 3 | import { UsersListComponent } from './usersList.component'; 4 | 5 | export { UsersCreateComponent, UsersEditComponent, UsersListComponent }; 6 | -------------------------------------------------------------------------------- /client/src/app/components/users/usersCreate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericCreateComponent } from 'src/app/components/generic'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { 5 | Email, 6 | Input, 7 | Password, 8 | Phone, 9 | TextInput, 10 | Username, 11 | } from 'src/app/form/Input'; 12 | import { MatSnackBar } from '@angular/material/snack-bar'; 13 | import { UserRouter } from 'src/app/router/UserRouter'; 14 | import { UsersService } from 'src/app/services/users.service'; 15 | import { RoleAutocompleteInput } from 'src/app/form/Queryable'; 16 | 17 | @Component({ 18 | selector: 'app-users-create', 19 | templateUrl: '../generic/form/generic-form.component.html', 20 | styleUrls: [], 21 | }) 22 | export class UsersCreateComponent extends GenericCreateComponent { 23 | public singularResource = 'user'; 24 | public routerHelper = UserRouter; 25 | public inputs: Input[] = [ 26 | new Username(), 27 | new Password(), 28 | new Email(), 29 | new Phone(), 30 | new RoleAutocompleteInput(), 31 | new TextInput({ 32 | label: 'Trigram', 33 | name: 'trigram', 34 | placeholder: 'JZN', 35 | }), 36 | new TextInput({ 37 | label: 'City', 38 | name: 'city', 39 | placeholder: 'Bikini bottom', 40 | }), 41 | ]; 42 | 43 | constructor( 44 | protected service: UsersService, 45 | protected router: Router, 46 | protected route: ActivatedRoute, 47 | protected snackBar: MatSnackBar 48 | ) { 49 | super(service, router, route, snackBar); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /client/src/app/components/users/usersEdit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericEditComponent } from 'src/app/components/generic'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { Email, Input, Phone, TextInput, Username } from 'src/app/form/Input'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { UserRouter } from 'src/app/router/UserRouter'; 7 | import { UsersService } from 'src/app/services/users.service'; 8 | 9 | @Component({ 10 | selector: 'app-users-edit', 11 | templateUrl: '../generic/form/generic-form.component.html', 12 | styleUrls: [], 13 | }) 14 | export class UsersEditComponent extends GenericEditComponent { 15 | public singularResource = 'user'; 16 | public routerHelper = UserRouter; 17 | public inputs: Input[] = [ 18 | new Username(), 19 | new Email(), 20 | new Phone(), 21 | new TextInput({ 22 | label: 'Trigram', 23 | name: 'trigram', 24 | placeholder: 'JZN', 25 | }), 26 | new TextInput({ 27 | label: 'City', 28 | name: 'city', 29 | placeholder: 'Bikini bottom', 30 | }), 31 | ]; 32 | 33 | constructor( 34 | protected service: UsersService, 35 | protected router: Router, 36 | protected route: ActivatedRoute, 37 | protected snackBar: MatSnackBar 38 | ) { 39 | super(service, router, route, snackBar); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /client/src/app/components/users/usersList.component.ts: -------------------------------------------------------------------------------- 1 | import { GenericListComponent, SHOW } from 'src/app/components/generic'; 2 | import { Component } from '@angular/core'; 3 | import { UsersService } from 'src/app/services/users.service'; 4 | import { Router } from '@angular/router'; 5 | import { UserRouter } from 'src/app/router/UserRouter'; 6 | 7 | @Component({ 8 | selector: 'app-users-list', 9 | templateUrl: '../generic/list/generic-list.component.html', 10 | styleUrls: [], 11 | }) 12 | export class UsersListComponent extends GenericListComponent { 13 | filters = ['id', 'username']; 14 | resource = 'users'; 15 | singularResource = 'User'; 16 | routerHelper = UserRouter; 17 | protected excludedFields = ['@id', '@type', SHOW.name]; 18 | 19 | constructor(protected service: UsersService, protected router: Router) { 20 | super(service, router); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/app/components/vulns/index.ts: -------------------------------------------------------------------------------- 1 | import { VulnsCreateComponent } from './vulnsCreate.component'; 2 | import { VulnsEditComponent } from './vulnsEdit.component'; 3 | import { VulnsListComponent } from './vulnsList.component'; 4 | 5 | export { VulnsCreateComponent, VulnsEditComponent, VulnsListComponent }; 6 | -------------------------------------------------------------------------------- /client/src/app/components/vulns/vulnsCreate.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { GenericCreateComponent } from 'src/app/components/generic'; 3 | import { ActivatedRoute, Router } from '@angular/router'; 4 | import { Input, Name, TextAreaInput } from 'src/app/form/Input'; 5 | import { MatSnackBar } from '@angular/material/snack-bar'; 6 | import { VulnsService } from 'src/app/services/vulns.service'; 7 | import { ImpactAutocompleteInput, VulnTypeAutocompleteInput, } from 'src/app/form/Queryable'; 8 | import { VulnRouter } from 'src/app/router/VulnRouter'; 9 | 10 | @Component({ 11 | selector: 'app-vulns-create', 12 | templateUrl: '../generic/form/generic-form.component.html', 13 | styleUrls: [], 14 | }) 15 | export class VulnsCreateComponent extends GenericCreateComponent { 16 | public singularResource = 'vuln'; 17 | public routerHelper = VulnRouter; 18 | public inputs: Input[] = []; 19 | 20 | constructor( 21 | protected service: VulnsService, 22 | protected router: Router, 23 | protected route: ActivatedRoute, 24 | protected snackBar: MatSnackBar, 25 | impactSelectInput: ImpactAutocompleteInput, 26 | vulnTypeSelectInput: VulnTypeAutocompleteInput 27 | ) { 28 | super(service, router, route, snackBar); 29 | this.inputs = [ 30 | new Name(), 31 | new TextAreaInput({ 32 | name: 'description', 33 | label: 'Description', 34 | placeholder: 'I found a vulnerability during...', 35 | }), 36 | new TextAreaInput({ 37 | name: 'remediation', 38 | label: 'Remediation', 39 | placeholder: 'You can correct this with...', 40 | }), 41 | impactSelectInput, 42 | vulnTypeSelectInput, 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/src/app/components/vulns/vulnsList.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { VulnsService } from 'src/app/services/vulns.service'; 4 | import { GenericListComponent, SHOW } from 'src/app/components/generic'; 5 | import { VulnRouter } from 'src/app/router/VulnRouter'; 6 | 7 | @Component({ 8 | selector: 'app-vulns-list', 9 | templateUrl: '../generic/list/generic-list.component.html', 10 | styleUrls: [], 11 | }) 12 | export class VulnsListComponent extends GenericListComponent { 13 | resource = 'vulns'; 14 | singularResource = 'Vulnerability'; 15 | routerHelper = VulnRouter; 16 | protected excludedFields = ['@id', '@type', SHOW.name, 'vulnType', 'impact']; 17 | 18 | constructor(protected service: VulnsService, protected router: Router) { 19 | super(service, router); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/app/file-information.ts: -------------------------------------------------------------------------------- 1 | export interface FileInformation { 2 | filename: string; 3 | size: number; 4 | } 5 | -------------------------------------------------------------------------------- /client/src/app/form/Date.ts: -------------------------------------------------------------------------------- 1 | import { Input } from './Input'; 2 | 3 | abstract class DateInput extends Input { 4 | public inputType = 'date'; 5 | 6 | constructor(props = {}) { 7 | super(); 8 | Object.entries(props).map(([k, v]) => { 9 | this[k.toString()] = v; 10 | }); 11 | } 12 | } 13 | 14 | export class RangeDateInput extends DateInput { 15 | public name = 'period'; 16 | public label = 'Enter a date range'; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/app/form/index.ts: -------------------------------------------------------------------------------- 1 | import * as QueryableModule from './Queryable'; 2 | 3 | export { QueryableModule }; 4 | -------------------------------------------------------------------------------- /client/src/app/guard/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate, Router } from '@angular/router'; 3 | import { DecodedToken } from 'src/app/storage/Token'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class AuthGuard implements CanActivate { 7 | constructor(private router: Router) {} 8 | 9 | canActivate(): boolean { 10 | if (new DecodedToken().get()) { 11 | return true; 12 | } 13 | 14 | this.router.navigate(['/login']); 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/app/guard/index.ts: -------------------------------------------------------------------------------- 1 | import { AuthGuard } from './auth.guard'; 2 | import { RoleGuard } from './role.guard'; 3 | 4 | export { AuthGuard, RoleGuard }; 5 | -------------------------------------------------------------------------------- /client/src/app/guard/role.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; 3 | import { DecodedToken } from 'src/app/storage/Token'; 4 | import { DashboardRouter } from 'src/app/router/DashboardRouter'; 5 | 6 | @Injectable({ providedIn: 'root' }) 7 | export class RoleGuard implements CanActivate { 8 | constructor(private router: Router) {} 9 | 10 | canActivate(route: ActivatedRouteSnapshot): boolean { 11 | if ( 12 | new DecodedToken().get() && 13 | new DecodedToken().getDecoded().roles.includes(route.data.role) 14 | ) { 15 | return true; 16 | } 17 | 18 | this.router.navigateByUrl( 19 | route.routeConfig.path === '' 20 | ? '/login' 21 | : DashboardRouter.redirectToList() 22 | ); 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/app/helpers/translation.ts: -------------------------------------------------------------------------------- 1 | import { TranslationFromAPIInterface } from 'src/app/model/Translation'; 2 | import { Locale } from 'src/app/storage/Locale'; 3 | 4 | export function getTranslation( 5 | translations: Record 6 | ): TranslationFromAPIInterface { 7 | if (!translations) { 8 | return undefined; 9 | } 10 | const locale = new Locale().get(); 11 | return ( 12 | translations[locale?.toString() ?? 'en'] ?? 13 | translations.en ?? 14 | translations[Object.keys(translations)[0]] 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /client/src/app/interceptor/index.ts: -------------------------------------------------------------------------------- 1 | import { UnauthorizedInterceptor } from './unauthorized.interceptor'; 2 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 3 | 4 | const interceptors = [UnauthorizedInterceptor].map((interceptor) => ({ 5 | provide: HTTP_INTERCEPTORS, 6 | useClass: interceptor, 7 | multi: true, 8 | })); 9 | 10 | export { interceptors }; 11 | -------------------------------------------------------------------------------- /client/src/app/interceptor/unauthorized.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | HttpInterceptor, 4 | HttpEvent, 5 | HttpRequest, 6 | HttpHandler, 7 | HttpErrorResponse, 8 | } from '@angular/common/http'; 9 | import { Observable, throwError } from 'rxjs'; 10 | import { Router } from '@angular/router'; 11 | import { catchError } from 'rxjs/operators'; 12 | import { DashboardRouter } from 'src/app/router/DashboardRouter'; 13 | 14 | @Injectable() 15 | export class UnauthorizedInterceptor implements HttpInterceptor { 16 | constructor(private router: Router) {} 17 | 18 | intercept( 19 | req: HttpRequest, 20 | next: HttpHandler 21 | ): Observable> { 22 | return next.handle(req).pipe( 23 | catchError((error: HttpErrorResponse) => { 24 | if (error && [401, 403].includes(error.status)) { 25 | this.router.navigateByUrl(DashboardRouter.redirectToList()); 26 | } 27 | return throwError(error); 28 | }) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/app/model/AbstractType.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractSerializerApplication, 5 | ObjectFromAPIInterface, 6 | } from 'src/app/model/abstract'; 7 | 8 | export interface AbstractTypeFromAPIInterface extends ObjectFromAPIInterface { 9 | name: string; 10 | } 11 | 12 | export class AbstractTypeSerializerApplication extends AbstractSerializerApplication { 13 | protected model = AbstractTypeModelApplication; 14 | } 15 | 16 | export class AbstractTypeModelApplication extends AbstractModelApplication { 17 | name: string; 18 | 19 | constructor(props: AbstractTypeFromAPIInterface) { 20 | super(props); 21 | this.name = props.name; 22 | } 23 | } 24 | 25 | export class AbstractTypeModelAPI extends AbstractModelAPI { 26 | name: string; 27 | 28 | constructor(props: AbstractTypeModelApplication) { 29 | super(props); 30 | this.name = props.name; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/app/model/Authentication.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractNormalizerApplication, 5 | AbstractSerializerApplication, 6 | ObjectFromAPIInterface, 7 | } from 'src/app/model/abstract'; 8 | 9 | export interface AuthenticationFromAPIInterface extends ObjectFromAPIInterface { 10 | username?: string; 11 | password?: string; 12 | token?: string; 13 | } 14 | 15 | export class AuthenticationSerializerApplication extends AbstractSerializerApplication { 16 | protected model = AuthenticationModelApplication; 17 | } 18 | 19 | export class AuthenticationNormalizerApplication extends AbstractNormalizerApplication { 20 | protected model = AuthenticationModelAPI; 21 | } 22 | 23 | export class AuthenticationModelApplication extends AbstractModelApplication { 24 | username?: string; 25 | password?: string; 26 | token?: string; 27 | 28 | constructor(props: AuthenticationFromAPIInterface) { 29 | super({ 30 | '@id': '', 31 | '@type': '' 32 | }); 33 | this.username = props.username; 34 | this.password = props.password; 35 | this.token = props.token; 36 | } 37 | } 38 | 39 | class AuthenticationModelAPI extends AbstractModelAPI { 40 | username: string; 41 | password: string; 42 | 43 | constructor(props: AuthenticationModelApplication) { 44 | super(props); 45 | this.username = props.username; 46 | this.password = props.password; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/src/app/model/Client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractNormalizerApplication, 5 | AbstractSerializerApplication, 6 | ObjectFromAPIInterface, 7 | } from 'src/app/model/abstract'; 8 | 9 | interface ClientFromAPIInterface extends ObjectFromAPIInterface { 10 | name: string; 11 | phone: string; 12 | firstName: string; 13 | lastName: string; 14 | mail: string; 15 | } 16 | 17 | export class ClientSerializerApplication extends AbstractSerializerApplication { 18 | protected model = ClientModelApplication; 19 | } 20 | 21 | export class ClientNormalizerApplication extends AbstractNormalizerApplication { 22 | protected model = ClientModelAPI; 23 | } 24 | 25 | export class ClientModelApplication extends AbstractModelApplication { 26 | name: string; 27 | phone: string; 28 | firstname: string; 29 | lastname: string; 30 | fullname: string; 31 | mail: string; 32 | 33 | constructor(props: ClientFromAPIInterface) { 34 | super(props); 35 | this.name = props.name; 36 | this.phone = props.phone; 37 | this.firstname = props.firstName; 38 | this.lastname = props.lastName; 39 | this.fullname = `${props.firstName} ${props.lastName}`; 40 | this.mail = props.mail; 41 | } 42 | } 43 | 44 | class ClientModelAPI extends AbstractModelAPI { 45 | name: string; 46 | phone: string; 47 | firstName: string; 48 | lastName: string; 49 | mail: string; 50 | 51 | constructor(props: ClientModelApplication) { 52 | super(props); 53 | this.name = props.name; 54 | this.phone = props.phone; 55 | this.firstName = props.firstname; 56 | this.lastName = props.lastname; 57 | this.mail = props.mail; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /client/src/app/model/Host.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractNormalizerApplication, 5 | AbstractSerializerApplication, 6 | ObjectFromAPIInterface, 7 | } from 'src/app/model/abstract'; 8 | 9 | export interface HostFromAPIInterface extends ObjectFromAPIInterface { 10 | name: string; 11 | hostVulns: string[]; 12 | checked: boolean; 13 | mission: string; 14 | technology: string; 15 | } 16 | 17 | export class HostSerializerApplication extends AbstractSerializerApplication { 18 | protected model = HostModelApplication; 19 | } 20 | 21 | export class HostNormalizerApplication extends AbstractNormalizerApplication { 22 | protected model = HostModelAPI; 23 | } 24 | 25 | export class HostModelApplication extends AbstractModelApplication { 26 | name: string; 27 | hostVulns: any[]; 28 | checked: boolean; 29 | mission: string; 30 | technology: string; 31 | 32 | constructor(props: HostFromAPIInterface) { 33 | super(props); 34 | this.name = props.name; 35 | this.hostVulns = props.hostVulns; 36 | this.mission = props.mission; 37 | this.technology = props.technology; 38 | } 39 | } 40 | 41 | class HostModelAPI extends AbstractModelAPI { 42 | name: string; 43 | technology: string; 44 | mission: string; 45 | checked: boolean; 46 | 47 | constructor(props: HostModelApplication) { 48 | super(props); 49 | this.name = props.name; 50 | this.technology = props.technology; 51 | this.mission = props.mission; 52 | this.checked = props.checked; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/src/app/model/Impact.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractNormalizerApplication, 5 | AbstractSerializerApplication, 6 | ObjectFromAPIInterface, 7 | } from 'src/app/model/abstract'; 8 | 9 | export const LOW = 'Low'; 10 | export const MEDIUM = 'Medium'; 11 | export const HIGH = 'High'; 12 | export const CRITICAL = 'Critical'; 13 | 14 | interface ImpactFromAPIInterface extends ObjectFromAPIInterface { 15 | name: string; 16 | } 17 | 18 | export class ImpactSerializerApplication extends AbstractSerializerApplication { 19 | protected model = ImpactModelApplication; 20 | } 21 | 22 | export class ImpactNormalizerApplication extends AbstractNormalizerApplication { 23 | protected model = ImpactModelAPI; 24 | } 25 | 26 | export class ImpactModelApplication extends AbstractModelApplication { 27 | name: string; 28 | 29 | constructor(props: ImpactFromAPIInterface) { 30 | super(props); 31 | this.name = props.name; 32 | } 33 | } 34 | 35 | class ImpactModelAPI extends AbstractModelAPI { 36 | name: string; 37 | 38 | constructor(props: ImpactModelApplication) { 39 | super(props); 40 | this.name = props.name; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/app/model/Media.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractSerializerApplication, 5 | ObjectFromAPIInterface, 6 | } from 'src/app/model/abstract'; 7 | 8 | interface ClientFromAPIInterface extends ObjectFromAPIInterface { 9 | name: string; 10 | } 11 | 12 | export class ClientSerializerApplication extends AbstractSerializerApplication { 13 | protected model = ClientModelApplication; 14 | } 15 | 16 | export class ClientModelApplication extends AbstractModelApplication { 17 | name: string; 18 | 19 | constructor(props: ClientFromAPIInterface) { 20 | super(props); 21 | this.name = props.name; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/app/model/Point.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractSerializerApplication, 5 | ObjectFromAPIInterface, 6 | } from 'src/app/model/abstract'; 7 | 8 | interface PointFromAPIInterface extends ObjectFromAPIInterface { 9 | name: string; 10 | description: string; 11 | } 12 | 13 | export class PointSerializerApplication extends AbstractSerializerApplication { 14 | protected model = PointModelApplication; 15 | } 16 | 17 | export class PointModelApplication extends AbstractModelApplication { 18 | name: string; 19 | description: string; 20 | 21 | constructor(props: PointFromAPIInterface) { 22 | super(props); 23 | this.name = props.name; 24 | this.description = props.description; 25 | } 26 | } 27 | 28 | class PointModelAPI extends AbstractModelAPI { 29 | name: string; 30 | description: string; 31 | 32 | constructor(props: PointModelApplication) { 33 | super(props); 34 | this.name = props.name; 35 | this.description = props.description; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/app/model/Step.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractNormalizerApplication, 5 | AbstractSerializerApplication, 6 | ObjectFromAPIInterface, 7 | } from 'src/app/model/abstract'; 8 | import { 9 | MissionModelApplication, 10 | MissionSerializerApplication, 11 | } from 'src/app/model/Mission'; 12 | 13 | interface StepFromAPIInterface extends ObjectFromAPIInterface { 14 | createdAt: string; 15 | description: string; 16 | findAt: string; 17 | } 18 | 19 | export class StepSerializerApplication extends AbstractSerializerApplication { 20 | protected model = StepModelApplication; 21 | } 22 | 23 | export class StepNormalizerApplication extends AbstractNormalizerApplication { 24 | protected model = StepModelAPI; 25 | } 26 | 27 | export class StepModelApplication extends AbstractModelApplication { 28 | createdAt: string; 29 | description: string; 30 | findAt: string; 31 | mission: string; 32 | 33 | constructor(props: StepFromAPIInterface) { 34 | super(props); 35 | this.createdAt = props.createdAt; 36 | this.description = props.description; 37 | this.findAt = props.findAt; 38 | } 39 | } 40 | 41 | class StepModelAPI extends AbstractModelAPI { 42 | createdAt: string; 43 | description: string; 44 | findAt: string; 45 | mission: string; 46 | 47 | constructor(props: StepModelApplication) { 48 | super(props); 49 | this.createdAt = props.createdAt; 50 | this.description = props.description; 51 | this.findAt = props.findAt; 52 | this.mission = props.mission; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/src/app/model/Translation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbstractModelAPI, 3 | AbstractModelApplication, 4 | AbstractSerializerApplication, 5 | ObjectFromAPIInterface, 6 | } from 'src/app/model/abstract'; 7 | 8 | export interface TranslationFromAPIInterface extends ObjectFromAPIInterface { 9 | locale: string; 10 | } 11 | 12 | export class TranslationSerializerApplication extends AbstractSerializerApplication { 13 | protected model = TranslationModelApplication; 14 | } 15 | 16 | export class TranslationModelApplication extends AbstractModelApplication { 17 | locale: string; 18 | 19 | constructor(props: TranslationFromAPIInterface) { 20 | super(props); 21 | this.locale = props.locale; 22 | } 23 | } 24 | 25 | export abstract class TranslationModelAPI extends AbstractModelAPI { 26 | locale: string; 27 | 28 | constructor(props: TranslationModelApplication) { 29 | super(props); 30 | this.locale = props.locale; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/app/model/VulnTranslation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TranslationFromAPIInterface, 3 | TranslationModelAPI, 4 | TranslationModelApplication, 5 | } from 'src/app/model/Translation'; 6 | import { 7 | AbstractNormalizerApplication, 8 | AbstractSerializerApplication, 9 | } from 'src/app/model/abstract'; 10 | 11 | export interface VulnTranslationFromAPIInterface 12 | extends TranslationFromAPIInterface { 13 | name: string; 14 | remediation: string; 15 | description: string; 16 | } 17 | 18 | export class VulnTranslationSerializerApplication extends AbstractSerializerApplication { 19 | protected model = VulnTranslationModelApplication; 20 | } 21 | 22 | export class VulnTranslationNormalizerApplication extends AbstractNormalizerApplication { 23 | protected model = VulnTranslationModelAPI; 24 | } 25 | 26 | export class VulnTranslationModelApplication extends TranslationModelApplication { 27 | description: string; 28 | locale: string; 29 | name: string; 30 | remediation: string; 31 | translatable: string; 32 | 33 | constructor(props: VulnTranslationFromAPIInterface) { 34 | super(props); 35 | this.description = props.description; 36 | this.name = props.name; 37 | this.remediation = props.remediation; 38 | } 39 | } 40 | 41 | class VulnTranslationModelAPI extends TranslationModelAPI { 42 | description: string; 43 | locale: string; 44 | name: string; 45 | remediation: string; 46 | translatable: string; 47 | 48 | constructor(props: VulnTranslationModelApplication) { 49 | super(props); 50 | this.description = props.description; 51 | this.locale = props.locale; 52 | this.name = props.name; 53 | this.remediation = props.remediation; 54 | this.translatable = props.translatable; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /client/src/app/model/abstract.ts: -------------------------------------------------------------------------------- 1 | export interface ObjectFromAPIInterface { 2 | '@id': string; 3 | '@type': string; 4 | } 5 | 6 | export class AbstractSerializerApplication { 7 | protected model = AbstractModelApplication; 8 | 9 | public serializeMany( 10 | items: ObjectFromAPIInterface[] 11 | ): AbstractModelApplication[] { 12 | return items.map((item) => new this.model(item)); 13 | } 14 | 15 | public serialize(item: ObjectFromAPIInterface): AbstractModelApplication { 16 | return new this.model(item); 17 | } 18 | } 19 | 20 | export class AbstractNormalizerApplication { 21 | protected model = AbstractModelAPI; 22 | 23 | public normalizeMany(items: AbstractModelApplication[]): AbstractModelAPI[] { 24 | return items.map((item) => new this.model(item)); 25 | } 26 | 27 | public normalize(item: AbstractModelApplication): AbstractModelAPI { 28 | return new this.model(item); 29 | } 30 | } 31 | 32 | export class AbstractModelApplication { 33 | public '@id': string; 34 | public '@type': string; 35 | public id: string; 36 | 37 | constructor(props: ObjectFromAPIInterface) { 38 | this['@id'] = props['@id']; 39 | this['@type'] = props['@type']; 40 | this.id = props['@id'].split('/').pop(); 41 | } 42 | } 43 | 44 | export class AbstractModelAPI { 45 | public '@id': string; 46 | 47 | constructor(props: AbstractModelApplication) { 48 | this['@id'] = props['@id']; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /client/src/app/resources/ClientResource.ts: -------------------------------------------------------------------------------- 1 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 2 | import { 3 | ClientsCreateComponent, 4 | ClientsEditComponent, 5 | ClientsListComponent, 6 | } from 'src/app/components/clients'; 7 | 8 | export class ClientResource extends AbstractResource { 9 | protected basePath = 'clients'; 10 | protected type = 'CLIENT'; 11 | protected list = ClientsListComponent; 12 | protected edit = ClientsEditComponent; 13 | protected create = ClientsCreateComponent; 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/resources/HostResource.ts: -------------------------------------------------------------------------------- 1 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 2 | import { 3 | HostsEditComponent, 4 | HostsListComponent, 5 | } from 'src/app/components/hosts'; 6 | 7 | export class HostResource extends AbstractResource { 8 | protected basePath = 'hosts'; 9 | protected type = 'HOST'; 10 | protected edit = HostsEditComponent; 11 | protected list = HostsListComponent; 12 | } 13 | -------------------------------------------------------------------------------- /client/src/app/resources/HostVulnResource.ts: -------------------------------------------------------------------------------- 1 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 2 | import { EditVulnWithStateComponent } from 'src/app/components/edit-vuln-with-state/edit-vuln-with-state.component'; 3 | 4 | export class HostVulnResource extends AbstractResource { 5 | protected basePath = 'host_vulns'; 6 | protected type = 'HOST_VULN'; 7 | protected edit = EditVulnWithStateComponent; 8 | } 9 | -------------------------------------------------------------------------------- /client/src/app/resources/ImpactResource.ts: -------------------------------------------------------------------------------- 1 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 2 | import { 3 | ImpactsCreateComponent, 4 | ImpactsEditComponent, 5 | ImpactsListComponent, 6 | } from 'src/app/components/impacts'; 7 | 8 | export class ImpactResource extends AbstractResource { 9 | protected basePath = 'impacts'; 10 | protected type = 'IMPACT'; 11 | protected create = ImpactsCreateComponent; 12 | protected edit = ImpactsEditComponent; 13 | protected list = ImpactsListComponent; 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/resources/MissionResource.ts: -------------------------------------------------------------------------------- 1 | import { Route } from '@angular/router'; 2 | import { MissionMyComponent } from 'src/app/components/mission-my/mission-my.component'; 3 | import { MissionSingleComponent } from 'src/app/components/mission-single/mission-single.component'; 4 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 5 | import { 6 | MissionsCreateComponent, 7 | MissionsEditComponent, 8 | MissionsListComponent, 9 | } from 'src/app/components/missions'; 10 | import { AddVulnsToHostExternalComponent } from 'src/app/components/add-vulns-to-host-external/add-vulns-to-host-external.component'; 11 | import { ADD_VULN_ROUTE } from 'src/app/router/MissionRouter'; 12 | import { AuthGuard, RoleGuard } from 'src/app/guard'; 13 | 14 | export class MissionResource extends AbstractResource { 15 | protected basePath = 'missions'; 16 | protected type = 'MISSION'; 17 | protected list = MissionMyComponent; 18 | protected show = MissionSingleComponent; 19 | protected edit = MissionsEditComponent; 20 | protected create = MissionsCreateComponent; 21 | 22 | generateResource(): Route { 23 | const resource = super.generateResource(); 24 | return { 25 | ...resource, 26 | children: [ 27 | { 28 | canActivate: [AuthGuard, RoleGuard], 29 | component: MissionsListComponent, 30 | data: { role: `ROLE_${this.type}_GET_LIST` }, 31 | path: 'all', 32 | }, 33 | { 34 | canActivate: [AuthGuard, RoleGuard], 35 | component: AddVulnsToHostExternalComponent, 36 | data: { role: `ROLE_${this.type}_PATCH` }, 37 | path: ADD_VULN_ROUTE, 38 | }, 39 | ...resource.children, 40 | ], 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client/src/app/resources/UserResource.ts: -------------------------------------------------------------------------------- 1 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 2 | import { 3 | UsersCreateComponent, 4 | UsersEditComponent, 5 | UsersListComponent, 6 | } from 'src/app/components/users'; 7 | 8 | export class UserResource extends AbstractResource { 9 | protected basePath = 'users'; 10 | protected type = 'USER'; 11 | protected create = UsersCreateComponent; 12 | protected edit = UsersEditComponent; 13 | protected list = UsersListComponent; 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/resources/VulnResource.ts: -------------------------------------------------------------------------------- 1 | import { AbstractResource } from 'src/app/resources/AbstractResource'; 2 | import { 3 | VulnsCreateComponent, 4 | VulnsEditComponent, 5 | VulnsListComponent, 6 | } from 'src/app/components/vulns'; 7 | 8 | export class VulnResource extends AbstractResource { 9 | protected basePath = 'vulnerabilities'; 10 | protected type = 'VULN'; 11 | protected list = VulnsListComponent; 12 | protected edit = VulnsEditComponent; 13 | protected create = VulnsCreateComponent; 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/router/ClientRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class ClientRouter extends AbstractRouter { 4 | protected static resource = 'clients'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/DashboardRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class DashboardRouter extends AbstractRouter { 4 | protected static resource = 'missions'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/HostRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class HostRouter extends AbstractRouter { 4 | protected static resource = 'hosts'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/HostVulnRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class HostVulnRouter extends AbstractRouter { 4 | protected static resource = 'host_vulns'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/ImpactRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class ImpactRouter extends AbstractRouter { 4 | protected static resource = 'impacts'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/MissionRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export const ADD_VULN_ROUTE = ':id/add-vuln/:targetHost'; 4 | 5 | export class MissionRouter extends AbstractRouter { 6 | protected static resource = 'missions'; 7 | 8 | public static redirectToAddVuln(id: string, targetHostIRI: string): string { 9 | return this.redirectTo( 10 | `/${ADD_VULN_ROUTE.replace(':id', id).replace( 11 | ':targetHost', 12 | this.getIdFromIRI(targetHostIRI) 13 | )}` 14 | ); 15 | } 16 | 17 | public static redirectToList(): string { 18 | return this.redirectTo('/all'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/app/router/UserRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class UserRouter extends AbstractRouter { 4 | protected static resource = 'users'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/VulnRouter.ts: -------------------------------------------------------------------------------- 1 | import { AbstractRouter } from 'src/app/router/router'; 2 | 3 | export class VulnRouter extends AbstractRouter { 4 | protected static resource = 'vulnerabilities'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/router/router.ts: -------------------------------------------------------------------------------- 1 | export const LIST_ROUTE = ''; 2 | export const CREATE_ROUTE = 'create'; 3 | export const EDIT_ROUTE = ':id/edit'; 4 | export const SHOW_ROUTE = ':id'; 5 | 6 | export abstract class AbstractRouter { 7 | protected static resource = ''; 8 | 9 | public static getIdFromIRI(iri: string): string { 10 | return iri.split('/').pop(); 11 | } 12 | 13 | public static redirectTo(params: string): string { 14 | return `/${this.resource}${params}`; 15 | } 16 | 17 | public static redirectToEdit(id: string): string { 18 | return this.redirectTo(`/${EDIT_ROUTE.replace(':id', id)}`); 19 | } 20 | 21 | public static redirectToEditFromIRI(id: string): string { 22 | return this.redirectToEdit(this.getIdFromIRI(id)); 23 | } 24 | 25 | public static redirectToCreate(): string { 26 | return this.redirectTo(`/${CREATE_ROUTE}`); 27 | } 28 | 29 | public static redirectToShow(id: string): string { 30 | return this.redirectTo(`/${SHOW_ROUTE.replace(':id', id)}`); 31 | } 32 | 33 | public static redirectToShowFromIRI(id: string): string { 34 | return this.redirectToShow(id.split('/').pop()); 35 | } 36 | 37 | public static redirectToList(): string { 38 | return this.redirectTo(LIST_ROUTE); 39 | } 40 | 41 | public static redirectToListFromIRI(): string { 42 | return this.redirectToList(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /client/src/app/services/clients.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractService } from 'src/app/services/abstract'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import { ClientNormalizerApplication, ClientSerializerApplication, } from 'src/app/model/Client'; 5 | 6 | @Injectable() 7 | export class ClientsService extends AbstractService { 8 | public serializer = new ClientSerializerApplication(); 9 | public normalizer = new ClientNormalizerApplication(); 10 | protected endpoint = 'clients'; 11 | 12 | constructor(protected http: HttpClient) { 13 | super(http); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/app/services/configService.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import burpConfig from 'src/assets/burp.json'; 3 | 4 | interface BurpInterface { 5 | target: { 6 | [key: string]: unknown; 7 | scope: { 8 | [key: string]: unknown; 9 | include: { enabled: boolean; prefix: string }[]; 10 | }; 11 | }; 12 | 13 | [key: string]: unknown; 14 | } 15 | 16 | @Injectable({ 17 | providedIn: 'root', 18 | }) 19 | export class ConfigService { 20 | public getBurpConfiguration(): BurpInterface { 21 | return burpConfig; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/app/services/connection.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { Router } from '@angular/router'; 4 | import { AbstractService } from 'src/app/services/abstract'; 5 | import { Token } from 'src/app/storage/Token'; 6 | import { 7 | AuthenticationModelApplication, 8 | AuthenticationNormalizerApplication, 9 | AuthenticationSerializerApplication, 10 | } from 'src/app/model/Authentication'; 11 | 12 | @Injectable({ 13 | providedIn: 'root', 14 | }) 15 | export class ConnectionService extends AbstractService { 16 | normalizer = new AuthenticationNormalizerApplication(); 17 | serializer = new AuthenticationSerializerApplication(); 18 | protected endpoint = 'authentication_token'; 19 | 20 | constructor(protected http: HttpClient, private router: Router) { 21 | super(http); 22 | } 23 | 24 | getOptions(): { headers: HttpHeaders } { 25 | return { 26 | headers: new HttpHeaders({ 27 | 'Content-Type': 'application/json; charset=utf-8', 28 | }), 29 | }; 30 | } 31 | 32 | getUrl(): string { 33 | return `${ConnectionService.getBaseAPI()}/${this.endpoint}`; 34 | } 35 | 36 | insert(data: any): Promise { 37 | return super.insert(data); 38 | } 39 | 40 | login(data: unknown): Promise<{ token: string }> { 41 | return this.insert(data).then(({ token }) => ({ token })); 42 | } 43 | 44 | logout(): void { 45 | new Token().reset(); 46 | this.router.navigateByUrl('/login'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/src/app/services/filter.service.ts: -------------------------------------------------------------------------------- 1 | import { AbstractService } from 'src/app/services/abstract'; 2 | 3 | export class FilterService { 4 | private timeout; 5 | private filters = {}; 6 | 7 | constructor(protected service: AbstractService) { 8 | } 9 | 10 | public applyFilter( 11 | filters: Record, 12 | callback: (params: Record) => void 13 | ): void { 14 | this.filters = {...this.filters, ...filters}; 15 | if (this.timeout !== null) { 16 | clearTimeout(this.timeout); 17 | } 18 | this.timeout = setTimeout(() => { 19 | callback(this.filters); 20 | this.timeout = null; 21 | }, 500); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/app/services/hostVulns.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractService } from 'src/app/services/abstract'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import { HostVulnNormalizerApplication, HostVulnSerializerApplication, } from 'src/app/model/HostVuln'; 5 | 6 | @Injectable() 7 | export class HostVulnsService extends AbstractService { 8 | public serializer = new HostVulnSerializerApplication(); 9 | public normalizer = new HostVulnNormalizerApplication(); 10 | protected endpoint = 'host_vulns'; 11 | 12 | constructor(protected http: HttpClient) { 13 | super(http); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/app/services/hosts.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractService } from 'src/app/services/abstract'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import { 5 | HostNormalizerApplication, 6 | HostSerializerApplication, 7 | } from 'src/app/model/Host'; 8 | 9 | @Injectable() 10 | export class HostsService extends AbstractService { 11 | public serializer = new HostSerializerApplication(); 12 | public normalizer = new HostNormalizerApplication(); 13 | protected endpoint = 'hosts'; 14 | 15 | constructor(protected http: HttpClient) { 16 | super(http); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/app/services/impacts.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { 5 | ImpactNormalizerApplication, 6 | ImpactSerializerApplication, 7 | } from 'src/app/model/Impact'; 8 | 9 | @Injectable({ 10 | providedIn: 'root', 11 | }) 12 | export class ImpactsService extends AbstractService { 13 | public serializer = new ImpactSerializerApplication(); 14 | public normalizer = new ImpactNormalizerApplication(); 15 | protected endpoint = 'impacts'; 16 | 17 | constructor(protected http: HttpClient) { 18 | super(http); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/src/app/services/locale.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { TranslateService } from '@ngx-translate/core'; 3 | import { Locale } from '../storage/Locale'; 4 | 5 | export enum Language { 6 | EN = 'en', 7 | ES = 'es', 8 | JP = 'jp', 9 | FR = 'fr', 10 | AR = 'ar', 11 | RU = 'ru', 12 | UA = 'ua', 13 | IT = 'it', 14 | } 15 | 16 | @Injectable() 17 | export class LocaleService { 18 | private readonly localStorage = new Locale(); 19 | 20 | /** 21 | * 22 | */ 23 | constructor(private translate: TranslateService) { 24 | this.initLanguage(); 25 | } 26 | 27 | public switchLanguage(lang: Language): void { 28 | this.translate.use(lang); 29 | this.localStorage.set(lang); 30 | } 31 | 32 | private initLanguage(): void { 33 | this.translate.setDefaultLang(Language.EN); // TODO find a correct place to put static constants 34 | const navigatorLang = 35 | navigator.language.length > 2 36 | ? navigator.language.slice(0, 2) 37 | : navigator.language; 38 | this.switchLanguage( 39 | (this.localStorage.get() as Language) || (navigatorLang as Language) 40 | ); // Get the current language from storage if not set get language from browser 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/app/services/mediaObjects.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractService } from 'src/app/services/abstract'; 3 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 4 | import { AbstractModelApplication } from 'src/app/model/abstract'; 5 | import { Token } from 'src/app/storage/Token'; 6 | 7 | @Injectable() 8 | export class MediaObjectsService extends AbstractService { 9 | protected endpoint = 'media_objects'; 10 | 11 | constructor(protected http: HttpClient) { 12 | super(http); 13 | } 14 | 15 | insert(data: FormData): Promise { 16 | return this.http 17 | .post(`${this.getUrl()}`, data, { 18 | headers: new HttpHeaders({ 19 | Authorization: `Bearer ${new Token().get()}`, 20 | }), 21 | }) 22 | .toPromise() 23 | .then((result: any) => this.serializer.serialize(result)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/app/services/medias.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractService } from 'src/app/services/abstract'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import { ClientSerializerApplication } from 'src/app/model/Client'; 5 | import { Observable } from 'rxjs'; 6 | 7 | @Injectable() 8 | export class MediasService extends AbstractService { 9 | public serializer = new ClientSerializerApplication(); 10 | protected endpoint = 'media'; 11 | 12 | constructor(protected http: HttpClient) { 13 | super(http); 14 | } 15 | 16 | getPictureByName(name: string): Observable { 17 | return this.http.get(`${this.getUrl()}/${name}`, this.getOptions()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/src/app/services/missions.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { 5 | MissionNormalizerApplication, 6 | MissionSerializerApplication, 7 | } from 'src/app/model/Mission'; 8 | 9 | @Injectable() 10 | export class MissionsService extends AbstractService { 11 | public serializer = new MissionSerializerApplication(); 12 | public normalizer = new MissionNormalizerApplication(); 13 | protected endpoint = 'missions'; 14 | 15 | constructor(protected http: HttpClient) { 16 | super(http); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/app/services/negative.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { PointSerializerApplication } from 'src/app/model/Point'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class NegativePointsService extends AbstractService { 10 | public serializer = new PointSerializerApplication(); 11 | protected endpoint = 'negative_points'; 12 | 13 | constructor(protected http: HttpClient) { 14 | super(http); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/app/services/positive.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { PointSerializerApplication } from 'src/app/model/Point'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class PositivePointsService extends AbstractService { 10 | public serializer = new PointSerializerApplication(); 11 | protected endpoint = 'positive_points'; 12 | 13 | constructor(protected http: HttpClient) { 14 | super(http); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/app/services/steps.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { 5 | StepNormalizerApplication, 6 | StepSerializerApplication, 7 | } from 'src/app/model/Step'; 8 | 9 | @Injectable() 10 | export class StepsService extends AbstractService { 11 | protected endpoint = 'steps'; 12 | public serializer = new StepSerializerApplication(); 13 | public normalizer = new StepNormalizerApplication(); 14 | 15 | constructor(protected http: HttpClient) { 16 | super(http); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/app/services/theme.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | import { ThemeStorage } from 'src/app/storage/Theme'; 4 | 5 | export enum Theme { 6 | DARK_THEME = 'dark-theme', 7 | LIGHT_THEME = '', 8 | } 9 | 10 | @Injectable() 11 | export class ThemeService { 12 | private _currentTheme: Theme = new ThemeStorage().get() as Theme || Theme.LIGHT_THEME; 13 | public readonly onChangeTheme = new BehaviorSubject(this._currentTheme); 14 | 15 | public get currentTheme(): Theme { 16 | return this._currentTheme; 17 | } 18 | 19 | public toggleTheme(): void { 20 | this._currentTheme = 21 | this._currentTheme === Theme.LIGHT_THEME 22 | ? Theme.DARK_THEME 23 | : Theme.LIGHT_THEME; 24 | this.onChangeTheme.next(this._currentTheme); 25 | new ThemeStorage().set(this._currentTheme) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/app/services/types.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { AbstractTypeSerializerApplication } from 'src/app/model/AbstractType'; 5 | 6 | @Injectable() 7 | export class TypesService extends AbstractService { 8 | serializer = new AbstractTypeSerializerApplication(); 9 | protected endpoint = 'mission_types'; 10 | 11 | constructor(protected http: HttpClient) { 12 | super(http); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/services/uploads.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import axios from 'axios'; 4 | import { AbstractService } from 'src/app/services/abstract'; 5 | 6 | @Injectable({ 7 | providedIn: 'root', 8 | }) 9 | export class UploadsService { 10 | private options; 11 | 12 | constructor(private http: HttpClient) { 13 | this.options = {}; 14 | } 15 | 16 | uploadHosts(data: any): any { 17 | return axios.post( 18 | `${AbstractService.getBaseAPIEndpoint()}/upload/host`, 19 | data, 20 | { 21 | headers: { 22 | 'Content-Type': 'multipart/form-data', 23 | Authorization: `Bearer ${localStorage.getItem('token')}`, 24 | }, 25 | } 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/app/services/users.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AbstractService } from 'src/app/services/abstract'; 3 | import { 4 | UserNormalizerApplication, 5 | UserSerializerApplication, 6 | } from 'src/app/model/User'; 7 | import { HttpClient } from '@angular/common/http'; 8 | 9 | @Injectable() 10 | export class UsersService extends AbstractService { 11 | public serializer = new UserSerializerApplication(); 12 | public normalizer = new UserNormalizerApplication(); 13 | protected endpoint = 'users'; 14 | 15 | constructor(protected http: HttpClient) { 16 | super(http); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/app/services/vulnTranslations.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Locale } from '../storage/Locale'; 4 | import { AbstractService } from 'src/app/services/abstract'; 5 | import { AbstractModelApplication } from 'src/app/model/abstract'; 6 | import { 7 | VulnTranslationNormalizerApplication, 8 | VulnTranslationSerializerApplication, 9 | } from 'src/app/model/VulnTranslation'; 10 | 11 | @Injectable({ 12 | providedIn: 'root', 13 | }) 14 | export class VulnTranslationsService extends AbstractService { 15 | public serializer = new VulnTranslationSerializerApplication(); 16 | public normalizer = new VulnTranslationNormalizerApplication(); 17 | protected endpoint = 'vuln_translations'; 18 | 19 | constructor(protected http: HttpClient) { 20 | super(http); 21 | } 22 | 23 | getData( 24 | params: Record = {} 25 | ): Promise<{ count: number; data: AbstractModelApplication[] }> { 26 | return super.getData({locale: new Locale().get(), ...params}); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/app/services/vulnTypes.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { AbstractTypeSerializerApplication } from 'src/app/model/AbstractType'; 5 | 6 | @Injectable() 7 | export class VulnTypesService extends AbstractService { 8 | serializer = new AbstractTypeSerializerApplication(); 9 | protected endpoint = 'vuln_types'; 10 | 11 | constructor(protected http: HttpClient) { 12 | super(http); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/services/vulns.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { AbstractService } from 'src/app/services/abstract'; 4 | import { Locale } from 'src/app/storage/Locale'; 5 | import { 6 | VulnNormalizerApplication, 7 | VulnSerializerApplication, 8 | } from 'src/app/model/Vuln'; 9 | import { AbstractModelApplication } from 'src/app/model/abstract'; 10 | 11 | @Injectable({ 12 | providedIn: 'root', 13 | }) 14 | export class VulnsService extends AbstractService { 15 | public serializer = new VulnSerializerApplication(); 16 | public normalizer = new VulnNormalizerApplication(); 17 | protected endpoint = 'vulns'; 18 | 19 | constructor(protected http: HttpClient) { 20 | super(http); 21 | } 22 | 23 | insert(data: any): Promise { 24 | const locale = new Locale().get(); 25 | return super.insert({ 26 | translations: { 27 | [locale]: { 28 | ...data, 29 | locale, 30 | }, 31 | }, 32 | ...data, 33 | }); 34 | } 35 | 36 | getData( 37 | params: Record = {} 38 | ): Promise<{ count: number; data: AbstractModelApplication[] }> { 39 | return super.getData({ locale: new Locale().get(), ...params }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /client/src/app/storage/AbstractStorage.ts: -------------------------------------------------------------------------------- 1 | export class AbstractStorage { 2 | protected key = ''; 3 | 4 | public get(): string { 5 | return localStorage.getItem(this.key) ?? ''; 6 | } 7 | 8 | public set(value: string): void { 9 | localStorage.setItem(this.key, value); 10 | } 11 | 12 | public reset(): void { 13 | localStorage.removeItem(this.key); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/app/storage/Locale.ts: -------------------------------------------------------------------------------- 1 | import { AbstractStorage } from './AbstractStorage'; 2 | 3 | export class Locale extends AbstractStorage { 4 | protected key = 'locale'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/storage/Theme.ts: -------------------------------------------------------------------------------- 1 | import { AbstractStorage } from './AbstractStorage'; 2 | 3 | export class ThemeStorage extends AbstractStorage { 4 | protected key = 'theme'; 5 | } 6 | -------------------------------------------------------------------------------- /client/src/app/storage/Token.ts: -------------------------------------------------------------------------------- 1 | import jwtDecode, { JwtPayload } from 'jwt-decode'; 2 | import { AbstractStorage } from './AbstractStorage'; 3 | 4 | export class Token extends AbstractStorage { 5 | protected key = 'token'; 6 | } 7 | 8 | interface SmershToken extends JwtPayload { 9 | roles: string[]; 10 | username: string; 11 | user: string; 12 | } 13 | 14 | export class DecodedToken extends Token { 15 | public getDecoded(): SmershToken { 16 | const token = this.get(); 17 | return token 18 | ? jwtDecode(this.get()) 19 | : { roles: [], username: '', user: '' }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/.gitkeep -------------------------------------------------------------------------------- /client/src/assets/Smersh.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/Smersh.docx -------------------------------------------------------------------------------- /client/src/assets/flags/ar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/ar.png -------------------------------------------------------------------------------- /client/src/assets/flags/en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/en.png -------------------------------------------------------------------------------- /client/src/assets/flags/es.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/es.png -------------------------------------------------------------------------------- /client/src/assets/flags/fr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/fr.png -------------------------------------------------------------------------------- /client/src/assets/flags/it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/it.png -------------------------------------------------------------------------------- /client/src/assets/flags/jp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/jp.png -------------------------------------------------------------------------------- /client/src/assets/flags/ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/ru.png -------------------------------------------------------------------------------- /client/src/assets/flags/ua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/flags/ua.png -------------------------------------------------------------------------------- /client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/assets/logo.png -------------------------------------------------------------------------------- /client/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | API_DOMAIN: 'api.smersh.lan', 4 | TRANSPORT: 'https://', 5 | API_ENDPOINT: '/api', 6 | MAPS_KEY: '', 7 | version: '1.2.0', 8 | environment: 'prod', 9 | }; 10 | -------------------------------------------------------------------------------- /client/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/client/src/favicon.ico -------------------------------------------------------------------------------- /client/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Front 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /client/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /client/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import 'theme.scss'; 3 | 4 | body { 5 | margin: 0; 6 | font-family: Roboto, "Helvetica Neue", sans-serif; 7 | } 8 | 9 | html, 10 | body { 11 | width: 100%; 12 | height: 100%; 13 | max-height: 100%; 14 | max-width: 100%; 15 | margin: 0; 16 | box-sizing: border-box; 17 | background-color: black 18 | } 19 | 20 | .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar { 21 | background-color: #ff002a; 22 | } 23 | 24 | 25 | .mat-drawer-content { 26 | margin-left: 240px; 27 | } 28 | 29 | 30 | .mat-slide-toggle.mat-checked .mat-slide-toggle-thumb { 31 | background-color: #ff002a; 32 | } 33 | 34 | .ml-auto { 35 | margin-left: auto; 36 | } 37 | 38 | .h-100 { 39 | height: 100%; 40 | } 41 | 42 | .w-100 { 43 | width: 100%; 44 | } 45 | 46 | .no-decoration { 47 | text-decoration: none; 48 | } 49 | 50 | .flex { 51 | display: flex; 52 | } 53 | 54 | .my-auto { 55 | margin-top: auto; 56 | margin-bottom: auto; 57 | } 58 | 59 | @for $i from 0 through 5 { 60 | .p-#{$i} { 61 | padding: $i+rem; 62 | } 63 | .pb-#{$i} { 64 | padding-bottom: $i+rem; 65 | } 66 | .pl-#{$i} { 67 | padding-left: $i+rem; 68 | } 69 | .pr-#{$i} { 70 | padding-right: $i+rem; 71 | } 72 | .pt-#{$i} { 73 | padding-top: $i+rem; 74 | } 75 | .px-#{$i} { 76 | padding-left: $i+rem; 77 | padding-right: $i+rem; 78 | } 79 | .py-#{$i} { 80 | padding-bottom: $i+rem; 81 | padding-top: $i+rem; 82 | } 83 | } 84 | 85 | .add-resource { 86 | padding-top: 1em; 87 | padding-bottom: 1.5em; 88 | } 89 | -------------------------------------------------------------------------------- /client/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/dist/zone-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 | keys(): string[]; 17 | (id: string): T; 18 | }; 19 | }; 20 | 21 | // First, initialize the Angular testing environment. 22 | getTestBed().initTestEnvironment( 23 | BrowserDynamicTestingModule, 24 | platformBrowserDynamicTesting() 25 | ); 26 | // Then we find all the tests. 27 | const context = require.context('./', true, /\.spec\.ts$/); 28 | // And load the modules. 29 | context.keys().map(context); 30 | -------------------------------------------------------------------------------- /client/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 | -------------------------------------------------------------------------------- /client/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 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "resolveJsonModule": true, 12 | "allowSyntheticDefaultImports": true, 13 | "moduleResolution": "node", 14 | "importHelpers": true, 15 | "target": "es2015", 16 | "module": "es2020", 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/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 | -------------------------------------------------------------------------------- /docker/dev-tls/Dockerfile: -------------------------------------------------------------------------------- 1 | # use this self-generated certificate only in dev, IT IS NOT SECURE! 2 | 3 | 4 | # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact 5 | ARG NGINX_VERSION=1.17 6 | 7 | 8 | FROM nginx:${NGINX_VERSION}-alpine 9 | 10 | # persistent / runtime deps 11 | RUN apk add --no-cache \ 12 | nss-tools \ 13 | ; 14 | 15 | WORKDIR /certs 16 | 17 | ARG MKCERT_VERSION=1.4.1 18 | RUN set -eux; \ 19 | wget -O /usr/local/bin/mkcert https://github.com/FiloSottile/mkcert/releases/download/v$MKCERT_VERSION/mkcert-v$MKCERT_VERSION-linux-amd64; \ 20 | chmod +x /usr/local/bin/mkcert; \ 21 | mkcert --cert-file localhost.crt --key-file localhost.key localhost 127.0.0.1 ::1 mercure; \ 22 | # the file must be named server.pem - the default certificate path in webpack-dev-server 23 | cat localhost.key localhost.crt > server.pem; \ 24 | # export the root CA cert, but not the root CA key 25 | cp "$(mkcert -CAROOT)/rootCA.pem" /certs/localCA.crt 26 | 27 | VOLUME /certs 28 | 29 | # add redirect from http://localhost to https://localhost 30 | RUN set -eux; \ 31 | { \ 32 | echo 'server {'; \ 33 | echo ' return 301 https://$host$request_uri;'; \ 34 | echo '}'; \ 35 | } | tee /etc/nginx/conf.d/default.conf 36 | -------------------------------------------------------------------------------- /img/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/api.png -------------------------------------------------------------------------------- /img/createMission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/createMission.png -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/demo.gif -------------------------------------------------------------------------------- /img/hacktivity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/hacktivity.png -------------------------------------------------------------------------------- /img/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/homepage.png -------------------------------------------------------------------------------- /img/rapport-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/rapport-preview.png -------------------------------------------------------------------------------- /img/rapport-preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/rapport-preview2.png -------------------------------------------------------------------------------- /img/searchbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/searchbar.png -------------------------------------------------------------------------------- /img/showMission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/img/showMission.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/logo.png -------------------------------------------------------------------------------- /smersh-body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/smersh-body.png -------------------------------------------------------------------------------- /smersh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matro7sh/Smersh/b0b15dadac5282edb60d79ccb5914eb026890e05/smersh.jpg -------------------------------------------------------------------------------- /traefik.toml: -------------------------------------------------------------------------------- 1 | [providers.docker] 2 | endpoint = "unix:///var/run/docker.sock" 3 | 4 | [api] 5 | insecure = true 6 | dashboard = true 7 | debug = true 8 | 9 | [entryPoints] 10 | [entryPoints.web] 11 | address = ":80" 12 | [entryPoints.web.http] 13 | [entryPoints.web.http.redirections] 14 | [entryPoints.web.http.redirections.entryPoint] 15 | to = "web-secure" 16 | scheme = "https" 17 | [entryPoints.web-secure] 18 | address = ":443" 19 | --------------------------------------------------------------------------------