├── .dockerignore ├── .editorconfig ├── .env ├── .env.development ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── defect-report.yml │ └── enhancement-request.yml ├── PULL_REQUEST_TEMPLATE.md ├── default-release-notes.md ├── dependabot.yml ├── images │ ├── i18n_i18n-ally-progress.png │ ├── i18n_i18n-ally-translate.png │ ├── i18n_language-picker.png │ ├── release-branch.png │ ├── release-master.png │ └── release-releasebranch.png ├── lock.yml ├── release.yml └── workflows │ ├── _meta-build.yaml │ ├── ci-build.yaml │ ├── ci-publish.yaml │ ├── ci-release.yaml │ ├── codeql-analysis.yaml │ ├── dependency-review.yaml │ ├── i18n.yaml │ └── lint.yaml ├── .gitignore ├── .idea └── icon.svg ├── .npmignore ├── .nvmrc ├── .postcssrc.js ├── .prettierrc.json ├── .vscode ├── extensions.json └── settings.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── RELEASING.md ├── SECURITY.md ├── babel.config.js ├── docker ├── Dockerfile.alpine ├── docker-compose.yml ├── docker-entrypoint.d │ ├── 10-listen-on-ipv6-by-default.sh │ └── 30-oidc-configuration.sh └── etc │ └── nginx │ └── templates │ └── default.conf.template ├── docs └── images │ ├── Frontend-Deployment.svg │ └── Frontend-Deployment.t2d ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── static │ ├── config.json │ └── oidc-callback.html ├── release.sh ├── snapshot.sh ├── src ├── App.vue ├── assets │ ├── .gitkeep │ ├── img │ │ ├── brand │ │ │ ├── dt-logo-symbol.svg │ │ │ ├── dt-logo-vertical-white-text.svg │ │ │ ├── dt-logo-white-text.svg │ │ │ └── owasp-logo-white.svg │ │ ├── github-logo.svg │ │ ├── openid-logo.svg │ │ ├── osi-logo.svg │ │ ├── osv-logo.png │ │ ├── snyk-logo.png │ │ └── trivy-logo.svg │ └── scss │ │ ├── _code.scss │ │ ├── _custom.scss │ │ ├── _ie-fix.scss │ │ ├── _variables.scss │ │ ├── style.scss │ │ └── vendors │ │ ├── _variables.scss │ │ ├── bootstrap │ │ └── _variables.scss │ │ ├── chart.js │ │ └── chart.scss │ │ └── vue-tags-input │ │ └── _vue-tags-input.scss ├── containers │ ├── DefaultContainer.vue │ ├── DefaultFooter.vue │ ├── DefaultHeader.vue │ └── DefaultHeaderProfileDropdown.vue ├── directives │ └── VuePermission.js ├── forms │ ├── BInputGroupFormDatepicker.vue │ ├── BInputGroupFormInput.vue │ ├── BInputGroupFormSelect.vue │ ├── BInputGroupFormSwitch.vue │ └── BValidatedInputGroupFormInput.vue ├── i18n │ ├── index.js │ └── locales │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── hi.json │ │ ├── it.json │ │ ├── ja.json │ │ ├── pl.json │ │ ├── pt-BR.json │ │ ├── pt.json │ │ ├── ru.json │ │ ├── uk-UA.json │ │ └── zh.json ├── main.js ├── mixins │ ├── availableClassifiersMixin.js │ ├── availableCollectionLogicsMixin.js │ ├── bootstrapTableMixin.js │ ├── globalVarsMixin.js │ ├── permissionsMixin.js │ └── routerMixin.js ├── plugins │ ├── jquery.js │ └── table.js ├── router │ └── index.js ├── shared │ ├── api.json │ ├── classes.js │ ├── common.js │ ├── eventbus.js │ ├── oidc.json │ ├── permissions.js │ ├── toggle-classes.js │ └── utils.js ├── validation │ └── index.js └── views │ ├── Dashboard.vue │ ├── administration │ ├── AdminMenu.vue │ ├── Administration.vue │ ├── accessmanagement │ │ ├── ApiKeyListGroupItem.vue │ │ ├── ChangePasswordModal.vue │ │ ├── CreateLdapUserModal.vue │ │ ├── CreateManagedUserModal.vue │ │ ├── CreateOidcGroupModal.vue │ │ ├── CreateOidcUserModal.vue │ │ ├── CreateTeamModal.vue │ │ ├── EditApiKeyCommentModal.vue │ │ ├── LdapUsers.vue │ │ ├── ManagedUsers.vue │ │ ├── OidcGroups.vue │ │ ├── OidcUsers.vue │ │ ├── Permissions.vue │ │ ├── PortfolioAccessControl.vue │ │ ├── SelectLdapGroupModal.vue │ │ ├── SelectOidcGroupModal.vue │ │ ├── SelectPermissionModal.vue │ │ ├── SelectProjectModal.vue │ │ ├── SelectTeamModal.vue │ │ └── Teams.vue │ ├── analyzers │ │ ├── InternalAnalyzer.vue │ │ ├── OssIndexAnalyzer.vue │ │ ├── SnykAnalyzer.vue │ │ ├── TrivyAnalyzer.vue │ │ └── VulnDbAnalyzer.vue │ ├── configuration │ │ ├── BomFormats.vue │ │ ├── Email.vue │ │ ├── EmailTestConfigurationModal.vue │ │ ├── Experimental.vue │ │ ├── General.vue │ │ ├── InternalComponents.vue │ │ ├── JiraConfig.vue │ │ ├── Search.vue │ │ ├── TaskScheduler.vue │ │ ├── Telemetry.vue │ │ └── WelcomeMessage.vue │ ├── integrations │ │ ├── DefectDojo.vue │ │ ├── FortifySsc.vue │ │ └── KennaSecurity.vue │ ├── mixins │ │ └── configPropertyMixin.js │ ├── notifications │ │ ├── Alerts.vue │ │ ├── CreateAlertModal.vue │ │ ├── CreateTemplateModal.vue │ │ ├── GeneralTemplateConfigurationModal.vue │ │ └── Templates.vue │ ├── repositories │ │ ├── Cargo.vue │ │ ├── Composer.vue │ │ ├── Cpan.vue │ │ ├── Gem.vue │ │ ├── GitHub.vue │ │ ├── GoModules.vue │ │ ├── Hackage.vue │ │ ├── Hex.vue │ │ ├── Maven.vue │ │ ├── Nixpkgs.vue │ │ ├── Npm.vue │ │ ├── Nuget.vue │ │ ├── Python.vue │ │ ├── Repositories.vue │ │ └── RepositoryCreateRepositoryModal.vue │ └── vuln-sources │ │ ├── EcosystemModal.vue │ │ ├── VulnSourceGitHubAdvisories.vue │ │ ├── VulnSourceNvd.vue │ │ └── VulnSourceOSVAdvisories.vue │ ├── audit │ └── PolicyViolationAudit.vue │ ├── components │ ├── AboutModal.vue │ ├── ActionableListGroupItem.vue │ ├── BToggleableDisplayButton.vue │ ├── ExternalReferencesDropdown.vue │ ├── LocalePicker.vue │ ├── PolicyViolationProgressBar.vue │ ├── ProfileEditModal.vue │ ├── SeverityProgressBar.vue │ ├── Showdown.vue │ └── SnapshotModal.vue │ ├── dashboard │ ├── ChartAuditingFindingsProgress.vue │ ├── ChartAuditingViolationsProgress.vue │ ├── ChartComponentVulnerabilities.vue │ ├── ChartEpssVsCvss.vue │ ├── ChartPolicyViolationBreakdown.vue │ ├── ChartPolicyViolationsClassification.vue │ ├── ChartPolicyViolationsState.vue │ ├── ChartPortfolioVulnerabilities.vue │ ├── ChartProjectVulnerabilities.vue │ ├── PortfolioWidgetRow.vue │ ├── SeverityBarChart.vue │ ├── WidgetInheritedRiskScore.vue │ ├── WidgetPortfolioVulnerabilities.vue │ ├── WidgetProjectsAtRisk.vue │ └── WidgetVulnerableComponents.vue │ ├── globalAudit │ ├── VulnerabilityAudit.vue │ ├── VulnerabilityAuditByOccurrence.vue │ └── VulnerabilityAuditGroupedByVulnerability.vue │ ├── modals │ └── InformationalModal.vue │ ├── pages │ ├── Login.vue │ ├── Page404.vue │ └── PasswordForceChange.vue │ ├── policy │ ├── CreateLicenseGroupModal.vue │ ├── CreatePolicyModal.vue │ ├── LicenseGroupList.vue │ ├── PolicyCondition.vue │ ├── PolicyList.vue │ ├── PolicyManagement.vue │ └── SelectLicenseModal.vue │ └── portfolio │ ├── components │ └── ComponentSearch.vue │ ├── licenses │ ├── License.vue │ ├── LicenseAddLicenseModal.vue │ └── LicenseList.vue │ ├── projects │ ├── Component.vue │ ├── ComponentCreatePropertyModal.vue │ ├── ComponentDashboard.vue │ ├── ComponentDetailsModal.vue │ ├── ComponentPropertiesModal.vue │ ├── ComponentVulnerabilities.vue │ ├── FindingAudit.vue │ ├── Project.vue │ ├── ProjectAddComponentModal.vue │ ├── ProjectAddVersionModal.vue │ ├── ProjectCollectionProjects.vue │ ├── ProjectComponents.vue │ ├── ProjectCreateProjectModal.vue │ ├── ProjectCreatePropertyModal.vue │ ├── ProjectDashboard.vue │ ├── ProjectDependencyGraph.vue │ ├── ProjectDetailsModal.vue │ ├── ProjectEpss.vue │ ├── ProjectFindings.vue │ ├── ProjectList.vue │ ├── ProjectListView.vue │ ├── ProjectPolicyViolations.vue │ ├── ProjectPropertiesModal.vue │ ├── ProjectServices.vue │ ├── ProjectUploadBomModal.vue │ ├── ProjectUploadVexModal.vue │ ├── SelectProjectModal.vue │ ├── Service.vue │ ├── ServiceDashboard.vue │ └── ServiceDetailsModal.vue │ ├── tags │ ├── SelectTagModal.vue │ ├── TagList.vue │ ├── TaggedCollectionProjectListModal.vue │ ├── TaggedNotificationRuleListModal.vue │ ├── TaggedPoliciesListModal.vue │ └── TaggedProjectListModal.vue │ └── vulnerabilities │ ├── AddAffectedComponentModal.vue │ ├── AffectedProjects.vue │ ├── SelectCweModal.vue │ ├── Vulnerability.vue │ ├── VulnerabilityCreateVulnerabilityModal.vue │ ├── VulnerabilityDetailsModal.vue │ └── VulnerabilityList.vue ├── update-embedded-version.js ├── vue-i18n-extract.config.js └── vue.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .git/ 3 | .github/ 4 | CODE_OF_CONDUCT.md 5 | LICENSE 6 | README.md 7 | SECURITY.md 8 | docker/ 9 | docs/ 10 | node_modules/ 11 | release.sh 12 | snapshot.sh 13 | !docker/etc/nginx/templates/default.conf.template 14 | !docker/docker-entrypoint.d/*.sh 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_I18N_LOCALE=en 3 | VUE_APP_I18N_FALLBACK_LOCALE=en 4 | VUE_APP_SERVER_URL= 5 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VUE_APP_I18N_LOCALE=en 3 | VUE_APP_I18N_FALLBACK_LOCALE=en 4 | VUE_APP_SERVER_URL=http://localhost:8080 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | parserOptions: { 7 | parser: '@babel/eslint-parser', 8 | }, 9 | extends: [ 10 | 'eslint:recommended', 11 | 'plugin:vue/essential', 12 | 'plugin:prettier/recommended', 13 | ], 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Slack Channel 4 | url: https://dependencytrack.org/slack 5 | about: Our Slack channel is the best way to get in touch! 6 | - name: GitHub Discussions 7 | url: https://github.com/DependencyTrack/dependency-track/discussions 8 | about: A good place to ask questions, share ideas and more! 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement-request.yml: -------------------------------------------------------------------------------- 1 | name: Enhancement Request 2 | description: File an enhancement request 3 | labels: [ "enhancement" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for helping us in making Dependency-Track better! 9 | 10 | **Is the enhancement you'd like to request *not* specifically UI related?** 11 | Please create an issue in the [main repository](https://github.com/DependencyTrack/dependency-track/issues/new/choose) instead! 12 | Unsure? That's fine, just report it in the main repo! The maintainers will migrate the issue if necessary. 13 | 14 | **Please do not ask questions here!** 15 | If you'd like to bounce ideas off or wonder whether what you want makes sense, 16 | you can reach out to maintainers and the broader community via various channels. 17 | Refer to our [contributing guidelines](https://github.com/DependencyTrack/dependency-track/blob/master/CONTRIBUTING.md#asking-questions) to find out where and how to ask questions. 18 | - type: textarea 19 | id: behavior-current 20 | attributes: 21 | label: Current Behavior 22 | description: |- 23 | Describe the current behavior that you observed. 24 | Consider providing screenshots, log output, and other supplementary data. 25 | 26 | *Files and images can be included via drag and drop into this text field.* 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: behavior-proposed 31 | attributes: 32 | label: Proposed Behavior 33 | description: >- 34 | Describe how you expect the Frontend to behave instead. 35 | Please include *why* you want the new behavior. 36 | validations: 37 | required: true 38 | - type: checkboxes 39 | id: checklist 40 | attributes: 41 | label: Checklist 42 | options: 43 | - label: I have read and understand the [contributing guidelines](https://github.com/DependencyTrack/dependency-track/blob/master/CONTRIBUTING.md#filing-issues) 44 | required: true 45 | - label: I have checked the [existing issues](https://github.com/DependencyTrack/frontend/issues) for whether this enhancement was already requested 46 | required: true 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 7 | 8 | ### Addressed Issue 9 | 10 | 16 | 17 | ### Additional Details 18 | 19 | 27 | 28 | ### Checklist 29 | 30 | 37 | 38 | - [ ] I have read and understand the [contributing guidelines](https://github.com/DependencyTrack/dependency-track/blob/master/CONTRIBUTING.md#pull-requests) 39 | - [ ] This PR introduces new or alters existing behavior, and I have updated the [documentation](https://github.com/DependencyTrack/dependency-track/tree/master/docs/_docs) accordingly 40 | -------------------------------------------------------------------------------- /.github/default-release-notes.md: -------------------------------------------------------------------------------- 1 | ### Dependency Track Frontend 2 | 3 | For official releases, refer to [Dependency Track Docs >> Changelogs](https://docs.dependencytrack.org/changelog/) for information about improvements and upgrade notes. 4 | If additional details are required, consult the closed issues for this release milestone. 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: docker 8 | directory: /docker 9 | schedule: 10 | interval: daily 11 | - package-ecosystem: github-actions 12 | directory: / 13 | schedule: 14 | interval: daily 15 | -------------------------------------------------------------------------------- /.github/images/i18n_i18n-ally-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/.github/images/i18n_i18n-ally-progress.png -------------------------------------------------------------------------------- /.github/images/i18n_i18n-ally-translate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/.github/images/i18n_i18n-ally-translate.png -------------------------------------------------------------------------------- /.github/images/i18n_language-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/.github/images/i18n_language-picker.png -------------------------------------------------------------------------------- /.github/images/release-branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/.github/images/release-branch.png -------------------------------------------------------------------------------- /.github/images/release-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/.github/images/release-master.png -------------------------------------------------------------------------------- /.github/images/release-releasebranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/.github/images/release-releasebranch.png -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for lock-threads - https://github.com/dessant/lock-threads 2 | daysUntilLock: 30 3 | exemptLabels: [] 4 | lockLabel: false 5 | lockComment: > 6 | This thread has been automatically locked since there has not been 7 | any recent activity after it was closed. Please open a new issue for 8 | related bugs. 9 | setLockReason: true 10 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: Enhancements 🚀 4 | labels: 5 | - enhancement 6 | - title: Bug Fixes 🐛 7 | labels: 8 | - defect 9 | - title: Dependency Updates 🤖 10 | labels: 11 | - dependencies 12 | - title: Other Changes 13 | labels: 14 | - "*" 15 | -------------------------------------------------------------------------------- /.github/workflows/ci-build.yaml: -------------------------------------------------------------------------------- 1 | name: Build CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' # Default branch 7 | - 'feature-**' # Feature branches 8 | pull_request: 9 | branches: 10 | - 'master' # Default branch 11 | - 'feature-**' # Feature branches 12 | workflow_dispatch: 13 | 14 | jobs: 15 | call-build: 16 | uses: ./.github/workflows/_meta-build.yaml 17 | with: 18 | app-version: 'snapshot' 19 | publish-container: ${{ github.ref_name == 'master' || startsWith(github.ref_name, 'feature-') }} 20 | ref-name: ${{ github.ref_name }} 21 | secrets: 22 | registry-0-usr: ${{ secrets.HUB_USERNAME }} 23 | registry-0-psw: ${{ secrets.HUB_ACCESS_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/workflows/ci-publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish CI 2 | 3 | on: 4 | release: 5 | types: 6 | - released 7 | workflow_dispatch: 8 | 9 | jobs: 10 | read-version: 11 | runs-on: ubuntu-latest 12 | outputs: 13 | version: ${{ steps.parse.outputs.version }} 14 | steps: 15 | - name: Assert ref type 16 | run: |- 17 | if [[ "$GITHUB_REF_TYPE" != "tag" ]]; then 18 | echo "::error::Publishing is only supported for tags!" 19 | exit 1 20 | fi 21 | 22 | - name: Checkout Repository 23 | uses: actions/checkout@v4.2.2 24 | 25 | - name: Parse Version from package.json 26 | id: parse 27 | run: |- 28 | VERSION=`jq -r '.version' package.json` 29 | echo "version=${VERSION}" >> $GITHUB_OUTPUT 30 | 31 | call-build: 32 | needs: 33 | - read-version 34 | uses: ./.github/workflows/_meta-build.yaml 35 | with: 36 | app-version: ${{ needs.read-version.outputs.version }} 37 | publish-container: true 38 | ref-name: ${{ github.ref_name }} 39 | secrets: 40 | registry-0-usr: ${{ secrets.HUB_USERNAME }} 41 | registry-0-psw: ${{ secrets.HUB_ACCESS_TOKEN }} 42 | 43 | update-github-release: 44 | runs-on: ubuntu-latest 45 | needs: 46 | - read-version 47 | - call-build 48 | steps: 49 | - name: Checkout Repository 50 | uses: actions/checkout@v4.2.2 51 | 52 | - name: Download Artifacts 53 | uses: actions/download-artifact@v4.3.0 54 | with: 55 | name: assembled-frontend 56 | 57 | - name: Create Checksums 58 | run: |- 59 | zip -qr frontend-dist.zip dist/* 60 | 61 | echo "# SHA1" >> checksums.txt 62 | sha1sum frontend-dist.zip >> checksums.txt 63 | echo "# SHA256" >> checksums.txt 64 | sha256sum frontend-dist.zip >> checksums.txt 65 | echo "# SHA512" >> checksums.txt 66 | sha512sum frontend-dist.zip >> checksums.txt 67 | 68 | - name: Update Release 69 | env: 70 | GITHUB_TOKEN: ${{ secrets.BOT_RELEASE_TOKEN }} 71 | run: |- 72 | cat << EOF >> .github/default-release-notes.md 73 | \`\`\`text 74 | $(cat checksums.txt) 75 | \`\`\` 76 | EOF 77 | 78 | gh release view ${{ needs.read-version.outputs.version }} \ 79 | --json body --jq .body >> .github/default-release-notes.md 80 | 81 | gh release edit ${{ needs.read-version.outputs.version }} \ 82 | --notes-file ".github/default-release-notes.md" 83 | 84 | gh release upload ${{ needs.read-version.outputs.version }} \ 85 | --clobber \ 86 | frontend-dist.zip \ 87 | checksums.txt \ 88 | bom.xml bom.json 89 | -------------------------------------------------------------------------------- /.github/workflows/ci-release.yaml: -------------------------------------------------------------------------------- 1 | name: Release CI 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version-to-bump: 7 | type: choice 8 | required: true 9 | description: 'Select which part of the version to bump and release' 10 | options: 11 | - patch 12 | - minor 13 | - major 14 | - prepatch 15 | - preminor 16 | - premajor 17 | - prerelease 18 | 19 | jobs: 20 | prepare-release: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout Repository 24 | uses: actions/checkout@v4.2.2 25 | 26 | - name: Set up NodeJs 27 | uses: actions/setup-node@v4.4.0 28 | with: 29 | node-version: '20' 30 | cache: 'npm' 31 | 32 | - name: Bump version and tag via NodeJS 33 | run: |- 34 | git config user.name "dependencytrack-bot" 35 | git config user.email "106437498+dependencytrack-bot@users.noreply.github.com" 36 | 37 | npm version ${{ github.event.inputs.version-to-bump }} -m "prepare-release: set version to %s" 38 | 39 | git push origin "HEAD:${{ github.ref }}" 40 | 41 | - name: Create GitHub Release 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.BOT_RELEASE_TOKEN }} 44 | GH_OPTS: '' 45 | run: |- 46 | VERSION=`jq -r '.version' package.json` 47 | 48 | if [[ "${{ contains(github.event.inputs.version-to-bump, 'pre') }}" == "true" ]]; then 49 | GH_OPTS="--prerelease" 50 | fi 51 | 52 | gh release create "${VERSION}" ${GH_OPTS} \ 53 | --target "${{ github.ref_name }}" \ 54 | --title "${VERSION}" \ 55 | --generate-notes 56 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yaml: -------------------------------------------------------------------------------- 1 | name: 'CodeQL' 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | - 'feature-**' 8 | pull_request: 9 | branches: 10 | - 'master' 11 | - 'feature-**' 12 | schedule: 13 | - cron: '0 7 * * 2' 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: ubuntu-latest 19 | if: ${{ github.repository == 'DependencyTrack/frontend' }} 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | # Override automatic language detection by changing the below list 25 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 26 | language: ['javascript'] 27 | # Learn more... 28 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4.2.2 33 | 34 | # Initializes the CodeQL tools for scanning. 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@v3 37 | with: 38 | languages: ${{ matrix.language }} 39 | # If you wish to specify custom queries, you can do so here or in a config file. 40 | # By default, queries listed here will override any specified in a config file. 41 | # Prefix the list here with "+" to use these queries and those in the config file. 42 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 43 | 44 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 45 | # If this step fails, then you should remove it and run the build manually (see below) 46 | - name: Autobuild 47 | uses: github/codeql-action/autobuild@v3 48 | 49 | # ℹ️ Command-line programs to run using the OS shell. 50 | # 📚 https://git.io/JvXDl 51 | 52 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 53 | # and modify them (or add more) to build your code if your project 54 | # uses a compiled language 55 | 56 | #- run: | 57 | # make bootstrap 58 | # make release 59 | 60 | - name: Perform CodeQL Analysis 61 | uses: github/codeql-action/analyze@v3 62 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yaml: -------------------------------------------------------------------------------- 1 | name: Dependency Review 2 | on: 3 | pull_request: 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | dependency-review: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v4.2.2 14 | 15 | - name: Dependency Review 16 | uses: actions/dependency-review-action@v4 17 | -------------------------------------------------------------------------------- /.github/workflows/i18n.yaml: -------------------------------------------------------------------------------- 1 | name: i18n 2 | on: 3 | push: 4 | branches: 5 | - 'master' # Default branch 6 | - 'feature-**' # Feature branches 7 | pull_request: 8 | branches: 9 | - 'master' # Default branch 10 | - 'feature-**' # Feature branches 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | check: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout Repository 21 | uses: actions/checkout@v4.2.2 22 | 23 | - name: Set up NodeJs 24 | uses: actions/setup-node@v4.4.0 25 | with: 26 | node-version: '20' 27 | cache: 'npm' 28 | 29 | - name: Install Dependencies 30 | run: npm install 31 | 32 | - name: Check for Translations 33 | run: | 34 | mv src/assets/scss/vendors/chart.js src/assets/scss/vendors/chart.js.skip 35 | npm run vue-i18n-extract 36 | continue-on-error: false 37 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: 5 | - 'master' # Default branch 6 | - 'feature-**' # Feature branches 7 | pull_request: 8 | branches: 9 | - 'master' # Default branch 10 | - 'feature-**' # Feature branches 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | lint: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout Repository 21 | uses: actions/checkout@v4.2.2 22 | 23 | - name: Set up NodeJs 24 | uses: actions/setup-node@v4.4.0 25 | with: 26 | node-version: '20' 27 | cache: 'npm' 28 | 29 | - name: Install Dependencies 30 | run: npm install 31 | 32 | - name: Lint Prettier 33 | run: npm run prettier 34 | continue-on-error: false 35 | 36 | - name: Lint ESLint 37 | run: npm run eslint 38 | continue-on-error: true 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /coverage 5 | 6 | /tests/e2e/reports/ 7 | selenium-debug.log 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw* 25 | 26 | # Generates as part of serve 27 | src/version.json 28 | 29 | # IntelliJ 30 | .idea/* 31 | !.idea/icon.svg 32 | -------------------------------------------------------------------------------- /.idea/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | # Exclude /dist. We want this included in the package when it's published 4 | # /dist 5 | /coverage 6 | 7 | /tests/e2e/reports/ 8 | selenium-debug.log 9 | 10 | # local env files 11 | .env.local 12 | .env.*.local 13 | 14 | # Log files 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | # Editor directories and files 20 | .idea 21 | .vscode 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw* 27 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonRecursiveSort": true, 3 | "plugins": ["prettier-plugin-sort-json"], 4 | "semi": true, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "all" 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "editorconfig.editorconfig", 6 | "github.vscode-github-actions", 7 | "lokalise.i18n-ally" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.alwaysSignOff": true, 3 | "[javascript]": { 4 | "editor.defaultFormatter": "esbenp.prettier-vscode" 5 | }, 6 | "[vue]": { 7 | "editor.defaultFormatter": "esbenp.prettier-vscode" 8 | }, 9 | "editor.codeActionsOnSave": { 10 | "source.fixAll.eslint": "explicit" 11 | }, 12 | "i18n-ally.translate.engines": ["google"], 13 | "i18n-ally.localesPaths": [ 14 | "src/i18n/locales" 15 | ], 16 | "i18n-ally.sourceLanguage": "en", 17 | "i18n-ally.keystyle": "nested" 18 | } 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at steve.springett@owasp.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing the OWASP Dependency-Track Frontend 2 | 3 | This document describes the process of releasing a new version of the Dependency-Track Frontend via GitHub Actions. 4 | 5 | ## Releasing 6 | 7 | ### Release a new major of minor version 8 | 9 | 1. Ensure the current state in `master` is ready to be released 10 | 2. Head over to the _Actions_ tab in GitHub 11 | 3. Select the _Release CI_ entry in the _Workflows_ section 12 | 4. The following UI element will have a button to trigger the workflow. Once clicked, the Use workflow from dialog will appear: 13 | 14 | ![Create a release from `master`](./.github/images/release-master.png) 15 | 16 | 5. Ensure that `master` is selected in the branch dropdown 17 | 6. For the part of the version to bump, select either `major` or `minor` (see [Semantic Versioning](https://semver.org/)) 18 | 7. Finally, once all inputs are checked press the _Run Workflow_ button 19 | 8. **Manually** create a release branch by selecting `master` in the branch dropdown and entering the branch name: 20 | 21 | ![Create a release branch](./.github/images/release-branch.png) 22 | 23 | ### Release a new bugfix version 24 | 25 | 1. Ensure the current state in the release branch is ready to be released 26 | 2. Head over to the _Actions_ tab in GitHub 27 | 3. Select the _Release CI_ entry in the _Workflows_ section 28 | 4. The following UI element will have a button to trigger the workflow. Once clicked, the Use workflow from dialog will appear: 29 | 30 | ![Create a release from a release branch](./.github/images/release-releasebranch.png) 31 | 32 | 5. Ensure that a release branch (e.g. `4.6.x`) is selected in the branch dropdown 33 | 6. For the part of the version to bump, select `patch` (see [Semantic Versioning](https://semver.org/)) 34 | 7. Finally, once all inputs are checked press the _Run Workflow_ button 35 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | The Dependency-Track team and community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. 4 | 5 | To report a security issue, email [security@dependencytrack.org](mailto:security@dependencytrack.org) and include the word "SECURITY" in the subject line. 6 | 7 | The Dependency-Track team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. 8 | 9 | Report security bugs in third-party modules to the person or team maintaining the module. 10 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@vue/babel-preset-jsx'], 4 | [ 5 | '@babel/preset-env', 6 | { 7 | useBuiltIns: 'entry', 8 | corejs: '3.33', 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /docker/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | FROM nginxinc/nginx-unprivileged:1.27.5-alpine@sha256:82a240b6d2f12d0154090b4a43425ea49c29f25e77683f36e52bca74da79bf0e 2 | 3 | # Arguments that can be passed at build time 4 | ARG COMMIT_SHA=unknown 5 | ARG APP_VERSION=0.0.0 6 | ARG APP_DIR=/opt/owasp/dependency-track-frontend/ 7 | 8 | ENV TZ=Etc/UTC \ 9 | LANG=C.UTF-8 \ 10 | # Set default settings that may get overridden to empty values by 11 | # the entrypoint script, if not explicitly provided by the user 12 | OIDC_SCOPE="openid profile email" \ 13 | BASE_PATH="/" 14 | USER root 15 | 16 | # Copy the static HTML and JS files to the application directory 17 | COPY ./dist ${APP_DIR} 18 | 19 | # Create the directorie where the frontend files will be deployed to (${APP_DIR}) 20 | # Ensure UID 101 & GID 0 own all the needed directories 21 | # Applying these changes allows the container to run via the OpenShift default SCC "Restricted" whereby arbitrary an UID and GID=0 are assigned 22 | RUN chown -R 101:0 ${APP_DIR} \ 23 | && chmod -R g=u ${APP_DIR} \ 24 | # add jq to easily manipulate config.json in entrypoint script 25 | && apk --no-cache add jq 26 | 27 | # Specify the user to run as (in numeric format for compatibility with Kubernetes/OpenShift's SCC) 28 | # Inherited from parent image 29 | # See https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/stable/alpine/Dockerfile#L139 30 | USER 101 31 | 32 | RUN mkdir /etc/nginx/templates 33 | # Setup entrypoint 34 | COPY --chown=101:0 ./docker/etc/nginx/templates/default.conf.template /etc/nginx/templates/default.conf.template 35 | 36 | COPY --chmod=755 ./docker/docker-entrypoint.d/ /docker-entrypoint.d/ 37 | 38 | 39 | # Specify the container working directory 40 | WORKDIR ${APP_DIR} 41 | 42 | # metadata labels 43 | LABEL \ 44 | org.opencontainers.image.vendor="OWASP" \ 45 | org.opencontainers.image.title="Official Dependency-Track Frontend Container image" \ 46 | org.opencontainers.image.description="Dependency-Track is an intelligent Component Analysis platform" \ 47 | org.opencontainers.image.version="${APP_VERSION}" \ 48 | org.opencontainers.image.url="https://dependencytrack.org/" \ 49 | org.opencontainers.image.source="https://github.com/DependencyTrack/frontend" \ 50 | org.opencontainers.image.revision="${COMMIT_SHA}" \ 51 | org.opencontainers.image.licenses="Apache-2.0" \ 52 | maintainer="steve.springett@owasp.org" 53 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | volumes: 4 | dependency-track-data: 5 | 6 | services: 7 | dtrack-apiserver: 8 | image: dependencytrack/apiserver:snapshot 9 | ports: 10 | - "8081:8080" 11 | volumes: 12 | - "dependency-track-data:/data" 13 | restart: unless-stopped 14 | 15 | dtrack-frontend: 16 | image: dependencytrack/frontend:snapshot 17 | depends_on: 18 | - dtrack-apiserver 19 | environment: 20 | - "API_BASE_URL=http://localhost:8081" 21 | # - "BASE_PATH=/" 22 | # - "OIDC_ISSUER=" 23 | # - "OIDC_CLIENT_ID=" 24 | # - "OIDC_SCOPE=" 25 | # - "OIDC_FLOW=" 26 | # - "OIDC_LOGIN_BUTTON_TEXT=" 27 | # volumes: 28 | # - "/host/path/to/config.json:/opt/owasp/dependency-track-frontend/static/config.json" 29 | ports: 30 | - "8080:8080" 31 | restart: unless-stopped 32 | 33 | -------------------------------------------------------------------------------- /docker/docker-entrypoint.d/10-listen-on-ipv6-by-default.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # vim:sw=4:ts=4:et 3 | 4 | # Copied and modified from: 5 | # https://github.com/nginxinc/docker-nginx-unprivileged/blob/1.25.2/entrypoint/10-listen-on-ipv6-by-default.sh 6 | 7 | set -e 8 | 9 | entrypoint_log() { 10 | if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then 11 | echo "$@" 12 | fi 13 | } 14 | 15 | ME=$(basename $0) 16 | DEFAULT_CONF_FILE="etc/nginx/templates/default.conf.template" 17 | 18 | # check if we have ipv6 available 19 | if [ ! -f "/proc/net/if_inet6" ]; then 20 | entrypoint_log "$ME: info: ipv6 not available" 21 | exit 0 22 | fi 23 | 24 | if [ ! -f "/$DEFAULT_CONF_FILE" ]; then 25 | entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist" 26 | exit 0 27 | fi 28 | 29 | # check if the file can be modified, e.g. not on a r/o filesystem 30 | touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; } 31 | 32 | # check if the file is already modified, e.g. on a container restart 33 | grep -q "listen \[::]\:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; } 34 | 35 | if [ -f "/etc/os-release" ]; then 36 | . /etc/os-release 37 | else 38 | entrypoint_log "$ME: info: can not guess the operating system" 39 | exit 0 40 | fi 41 | 42 | # Modified from original by nscuro: 43 | # Do not check whether the default configuration file has been changed vs 44 | # what is packaged with the distribution's installation. We customized the 45 | # file and want the changes the applied regardless. 46 | 47 | #entrypoint_log "$ME: info: Getting the checksum of /$DEFAULT_CONF_FILE" 48 | # 49 | #case "$ID" in 50 | # "debian") 51 | # CHECKSUM=$(dpkg-query --show --showformat='${Conffiles}\n' nginx | grep $DEFAULT_CONF_FILE | cut -d' ' -f 3) 52 | # echo "$CHECKSUM /$DEFAULT_CONF_FILE" | md5sum -c - >/dev/null 2>&1 || { 53 | # entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" 54 | # exit 0 55 | # } 56 | # ;; 57 | # "alpine") 58 | # CHECKSUM=$(apk manifest nginx 2>/dev/null| grep $DEFAULT_CONF_FILE | cut -d' ' -f 1 | cut -d ':' -f 2) 59 | # echo "$CHECKSUM /$DEFAULT_CONF_FILE" | sha1sum -c - >/dev/null 2>&1 || { 60 | # entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE differs from the packaged version" 61 | # exit 0 62 | # } 63 | # ;; 64 | # *) 65 | # entrypoint_log "$ME: info: Unsupported distribution" 66 | # exit 0 67 | # ;; 68 | #esac 69 | 70 | # enable ipv6 on default.conf listen sockets 71 | sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE 72 | 73 | entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE" 74 | 75 | exit 0 76 | -------------------------------------------------------------------------------- /docker/docker-entrypoint.d/30-oidc-configuration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | entrypoint_log() { 6 | if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then 7 | echo "$@" 8 | fi 9 | } 10 | 11 | ME=$(basename $0) 12 | 13 | if ! touch ./static/config.json 2>/dev/null; then 14 | entrypoint_log "$ME: info: can not modify config.json - ENV configuration will be ignored" 15 | else 16 | CONFIG=$(jq '.API_BASE_URL = env.API_BASE_URL 17 | | .API_WITH_CREDENTIALS = env.API_WITH_CREDENTIALS 18 | | .OIDC_ISSUER = env.OIDC_ISSUER 19 | | .OIDC_CLIENT_ID = env.OIDC_CLIENT_ID 20 | | .OIDC_SCOPE = env.OIDC_SCOPE 21 | | .OIDC_FLOW = env.OIDC_FLOW 22 | | .OIDC_LOGIN_BUTTON_TEXT = env.OIDC_LOGIN_BUTTON_TEXT' \ 23 | ./static/config.json) 24 | echo "${CONFIG}" > ./static/config.json 25 | entrypoint_log "$ME: info: effective config: $(echo "${CONFIG}" | jq -c '.')" 26 | fi 27 | 28 | exec "$@" 29 | -------------------------------------------------------------------------------- /docker/etc/nginx/templates/default.conf.template: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | server_name _; 4 | 5 | location / { 6 | root /opt/owasp/dependency-track-frontend; 7 | index index.html; 8 | try_files $uri $uri/ /index.html; 9 | 10 | location ~ (config\.json|index\.html)$ { 11 | add_header Cache-Control "max-age=0, no-cache, no-store, must-revalidate"; 12 | add_header Pragma "no-cache"; 13 | add_header Expires 0; 14 | } 15 | 16 | sub_filter ' 1%", 99 | "last 2 versions", 100 | "not ie <= 10" 101 | ], 102 | "engines": { 103 | "node": ">= 20", 104 | "npm": ">= 9" 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dependency-Track 8 | 9 | 10 | 11 | 17 |
18 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /public/static/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "API_BASE_URL": "", 3 | "API_WITH_CREDENTIALS": "", 4 | "OIDC_CLIENT_ID": "", 5 | "OIDC_FLOW": "code", 6 | "OIDC_ISSUER": "", 7 | "OIDC_LOGIN_BUTTON_TEXT": "", 8 | "OIDC_SCOPE": "openid email profile" 9 | } 10 | -------------------------------------------------------------------------------- /public/static/oidc-callback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Waiting... 6 | 7 | 8 | 9 | 10 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clear 4 | RELEASE_TYPE=patch 5 | PS3='Select the type of release to perform: ' 6 | releaseTypes=("Major" "Minor" "Patch" "Quit") 7 | select opt in "${releaseTypes[@]}" 8 | do 9 | case $opt in 10 | "Major") 11 | RELEASE_TYPE=major 12 | break 13 | ;; 14 | "Minor") 15 | RELEASE_TYPE=minor 16 | break 17 | ;; 18 | "Patch") 19 | RELEASE_TYPE=patch 20 | break 21 | ;; 22 | "Quit") 23 | break 24 | ;; 25 | *) echo "invalid option $REPLY";; 26 | esac 27 | done 28 | 29 | npm version $RELEASE_TYPE 30 | if [[ "$?" -ne 0 ]] ; then 31 | echo 'Aborting release due to version incremental failure. Ensure there are no modified/uncommitted files in the repo'; exit $rc 32 | fi 33 | 34 | npm run build 35 | if [[ "$?" -ne 0 ]] ; then 36 | echo 'Aborting release due to build failure'; exit $rc 37 | fi 38 | 39 | PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]') 40 | zip -r frontend-dist.zip dist/* 41 | echo Publishing $PACKAGE_VERSION to GitHub Releases 42 | # pip install githubrelease 43 | # https://github.com/j0057/github-release 44 | githubrelease release dependencytrack/frontend create $PACKAGE_VERSION \ 45 | --name $PACKAGE_VERSION --body "Dependency-Track Frontend" \ 46 | --publish bom.xml bom.json frontend-dist.zip 47 | 48 | REPO=dependencytrack/frontend 49 | docker rmi $REPO:latest 50 | docker rmi $REPO:$PACKAGE_VERSION 51 | docker login 52 | docker build --no-cache --pull -f docker/Dockerfile -t $REPO:$PACKAGE_VERSION -t $REPO:latest --platform linux/amd64,linux/arm64 --push . 53 | -------------------------------------------------------------------------------- /snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clear 4 | 5 | npm run build 6 | if [[ "$?" -ne 0 ]] ; then 7 | echo 'Aborting snapshot release due to build failure'; exit $rc 8 | fi 9 | 10 | PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]') 11 | zip -r frontend-dist.zip dist/* 12 | echo Publishing $PACKAGE_VERSION to GitHub Releases 13 | # pip install githubrelease 14 | # https://github.com/j0057/github-release 15 | githubrelease release dependencytrack/frontend create $PACKAGE_VERSION \ 16 | --name $PACKAGE_VERSION --body "Dependency-Track Frontend" \ 17 | --publish bom.xml frontend-dist.zip \ 18 | --prerelease 19 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/img/brand/dt-logo-symbol.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/img/osv-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/src/assets/img/osv-logo.png -------------------------------------------------------------------------------- /src/assets/img/snyk-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DependencyTrack/frontend/6d39df85b5eaac63ad771be116f413a5f6e74c0c/src/assets/img/snyk-logo.png -------------------------------------------------------------------------------- /src/assets/scss/_code.scss: -------------------------------------------------------------------------------- 1 | code, 2 | pre { 3 | font-family: "Courier 10 Pitch", Courier, monospace; 4 | } 5 | 6 | // Inline code 7 | code { 8 | padding: 2px 4px; 9 | font-size: 1.1em; 10 | color: #e83e8c; 11 | background-color: $grey-750; 12 | } 13 | 14 | // Blocks of code 15 | pre { 16 | display: block; 17 | padding: 1em; 18 | font-size: 1.1em; 19 | line-height: 1; 20 | color: $light-blue; 21 | word-break: break-all; 22 | word-wrap: break-word; 23 | background-color: $grey-750; 24 | border: 1px solid $grey-900; 25 | border-radius: 0.25rem; 26 | 27 | code { 28 | font-size: inherit; 29 | color: inherit; 30 | white-space: pre-wrap; 31 | background-color: transparent; 32 | border-radius: 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/scss/_ie-fix.scss: -------------------------------------------------------------------------------- 1 | //IE fix for sticky footer 2 | @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { 3 | .app { 4 | height: 100%; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/scss/style.scss: -------------------------------------------------------------------------------- 1 | // If you want to override variables do it here 2 | @import "variables"; 3 | 4 | // Import styles 5 | @import "~@coreui/coreui/scss/coreui"; 6 | 7 | @import '~vue-easy-pie-chart/dist/vue-easy-pie-chart.css'; 8 | 9 | @import "code"; 10 | 11 | // If you want to add something do it here 12 | @import "custom"; 13 | 14 | // ie fixes 15 | @import "ie-fix"; 16 | 17 | @import "~vue-multiselect/dist/vue-multiselect.min.css"; 18 | -------------------------------------------------------------------------------- /src/assets/scss/vendors/_variables.scss: -------------------------------------------------------------------------------- 1 | // Override Boostrap variables 2 | @import "../variables"; 3 | @import "../vendors/bootstrap/variables"; 4 | @import "~bootstrap/scss/mixins"; 5 | @import "~@coreui/coreui/scss/variables"; 6 | -------------------------------------------------------------------------------- /src/assets/scss/vendors/chart.js/chart.scss: -------------------------------------------------------------------------------- 1 | // Import variables 2 | @import '../variables'; 3 | 4 | .chart-legend, 5 | .bar-legend, 6 | .line-legend, 7 | .pie-legend, 8 | .radar-legend, 9 | .polararea-legend, 10 | .doughnut-legend { 11 | list-style-type: none; 12 | margin-top: 5px; 13 | text-align: center; 14 | -webkit-padding-start: 0; 15 | -moz-padding-start: 0; 16 | padding-left: 0; 17 | } 18 | .chart-legend li, 19 | .bar-legend li, 20 | .line-legend li, 21 | .pie-legend li, 22 | .radar-legend li, 23 | .polararea-legend li, 24 | .doughnut-legend li { 25 | display: inline-block; 26 | white-space: nowrap; 27 | position: relative; 28 | margin-bottom: 4px; 29 | @include border-radius($border-radius); 30 | padding: 2px 8px 2px 28px; 31 | font-size: smaller; 32 | cursor: default; 33 | } 34 | .chart-legend li span, 35 | .bar-legend li span, 36 | .line-legend li span, 37 | .pie-legend li span, 38 | .radar-legend li span, 39 | .polararea-legend li span, 40 | .doughnut-legend li span { 41 | display: block; 42 | position: absolute; 43 | left: 0; 44 | top: 0; 45 | width: 20px; 46 | height: 20px; 47 | @include border-radius($border-radius); 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/scss/vendors/vue-tags-input/_vue-tags-input.scss: -------------------------------------------------------------------------------- 1 | // If you want to override variables do it here 2 | @import "../../variables"; 3 | 4 | /* style the background and the text color of the input ... */ 5 | .vue-tags-input { 6 | } 7 | 8 | .vue-tags-input .ti-new-tag-input { 9 | background: transparent; 10 | color: #ffffff; 11 | } 12 | 13 | .vue-tags-input .ti-input { 14 | background: $tags-input-bg; 15 | padding: 4px 10px; 16 | transition: border-bottom 200ms ease; 17 | border: 1px solid $border-color; 18 | border-radius: 0.25rem; 19 | } 20 | 21 | /* we cange the border color if the user focuses the input */ 22 | .vue-tags-input.ti-focus .ti-input { 23 | border: 1px solid $light-blue; 24 | box-shadow: 0 0 0 0.2rem rgba(99, 194, 222, 0.25); 25 | } 26 | 27 | /* some stylings for the autocomplete layer */ 28 | .vue-tags-input div.ti-autocomplete { 29 | background: #283944; 30 | border: 1px solid $tags-input-border-color; 31 | border-top: none; 32 | border-radius: 0.25rem; 33 | } 34 | 35 | /* the selected item in the autocomplete layer, should be highlighted */ 36 | .vue-tags-input .ti-item.ti-selected-item { 37 | background: $light-blue; 38 | color: #283944; 39 | } 40 | 41 | /* style the placeholders color across all browser */ 42 | .vue-tags-input ::-webkit-input-placeholder { 43 | color: #a4b1b6; 44 | } 45 | 46 | .vue-tags-input ::-moz-placeholder { 47 | color: #a4b1b6; 48 | } 49 | 50 | .vue-tags-input :-ms-input-placeholder { 51 | color: #a4b1b6; 52 | } 53 | 54 | .vue-tags-input :-moz-placeholder { 55 | color: #a4b1b6; 56 | } 57 | 58 | /* default styles for all the tags */ 59 | .vue-tags-input .ti-tag { 60 | position: relative; 61 | color: $light-blue; 62 | background-color: transparent; 63 | border: 1px solid $light-blue; 64 | } 65 | 66 | /* we defined a custom css class in the data model, now we are using it to style the tag */ 67 | .vue-tags-input .ti-tag.custom-class { 68 | background: transparent; 69 | border: 1px solid $light-blue; 70 | color: $light-blue; 71 | margin-right: 4px; 72 | border-radius: 0.25rem; 73 | font-size: 13px; 74 | } 75 | 76 | /* the styles if a tag is invalid */ 77 | .vue-tags-input .ti-tag.ti-invalid { 78 | background-color: $red; 79 | } 80 | 81 | /* if the user input is invalid, the input color should be red */ 82 | .vue-tags-input .ti-new-tag-input.ti-invalid { 83 | color: $red; 84 | } 85 | 86 | /* if a tag or the user input is a duplicate, it should be crossed out */ 87 | .vue-tags-input .ti-duplicate span, 88 | .vue-tags-input .ti-new-tag-input.ti-duplicate { 89 | text-decoration: line-through; 90 | } 91 | 92 | /* if the user presses backspace, the complete tag should be crossed out, to mark it for deletion */ 93 | .vue-tags-input .ti-tag:after { 94 | transition: transform .2s; 95 | position: absolute; 96 | content: ''; 97 | height: 2px; 98 | width: 108%; 99 | left: -4%; 100 | top: calc(50% - 1px); 101 | background-color: #000; 102 | transform: scaleX(0); 103 | } 104 | 105 | .vue-tags-input .ti-deletion-mark:after { 106 | transform: scaleX(1); 107 | } 108 | -------------------------------------------------------------------------------- /src/containers/DefaultFooter.vue: -------------------------------------------------------------------------------- 1 | 14 | 28 | 34 | -------------------------------------------------------------------------------- /src/containers/DefaultHeader.vue: -------------------------------------------------------------------------------- 1 | 18 | 32 | -------------------------------------------------------------------------------- /src/containers/DefaultHeaderProfileDropdown.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 75 | 76 | 86 | -------------------------------------------------------------------------------- /src/directives/VuePermission.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Permissions Vue Directive 3 | */ 4 | import Vue from 'vue'; 5 | import { hasPermission, decodeToken, getToken } from '../shared/permissions'; 6 | 7 | Vue.directive('permission', function (el, binding) { 8 | let decodedToken = decodeToken(getToken()); 9 | if (Array.isArray(binding.value)) { 10 | let permitted = false; 11 | if (binding.arg === 'and') { 12 | // This is the AND case. If a user has ALL of the specified permissions, permitted will be true 13 | permitted = true; 14 | binding.value.forEach(function (b) { 15 | if (!hasPermission(b, decodedToken)) { 16 | permitted = false; 17 | } 18 | }); 19 | } else if (binding.arg === 'or') { 20 | // This is the OR case. If a user has one or more of the specified permissions, permitted will be true 21 | binding.value.forEach(function (b) { 22 | if (hasPermission(b, decodedToken)) { 23 | permitted = true; 24 | } 25 | }); 26 | } 27 | if (!permitted) { 28 | el.style.display = 'none'; 29 | } 30 | } else { 31 | if (!hasPermission(binding.value, decodedToken)) { 32 | el.style.display = 'none'; 33 | } 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /src/forms/BInputGroupFormInput.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 133 | -------------------------------------------------------------------------------- /src/forms/BInputGroupFormSelect.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 115 | -------------------------------------------------------------------------------- /src/forms/BInputGroupFormSwitch.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 81 | 93 | -------------------------------------------------------------------------------- /src/forms/BValidatedInputGroupFormInput.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 114 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueI18n from 'vue-i18n'; 3 | import axios from 'axios'; 4 | import api from '../shared/api.json'; 5 | 6 | Vue.use(VueI18n); 7 | 8 | async function getDefaultLanguage() { 9 | try { 10 | let url = `${api.BASE_URL}/${api.URL_CONFIG_PROPERTY}/public/general/default.locale`; 11 | let response = await axios.get(url); 12 | return decodeURIComponent(response.data.propertyValue); 13 | } catch (error) { 14 | console.error('Error fetching default language:', error); 15 | return ''; 16 | } 17 | } 18 | 19 | function loadLocaleMessages() { 20 | const locales = require.context( 21 | './locales', 22 | true, 23 | /[A-Za-z0-9-_,\s]+\.json$/i, 24 | ); 25 | const messages = {}; 26 | locales.keys().forEach((key) => { 27 | const matched = key.match(/([A-Za-z0-9-_]+)\./i); 28 | if (matched && matched.length > 1) { 29 | const locale = matched[1]; 30 | messages[locale] = locales(key); 31 | } 32 | }); 33 | return messages; 34 | } 35 | 36 | const localeMessages = loadLocaleMessages(); 37 | 38 | function matchLocale(requestedLocale) { 39 | console.log(localeMessages); 40 | let exactMatch = Object.keys(localeMessages).find( 41 | (locale) => requestedLocale === locale, 42 | ); 43 | if (exactMatch) { 44 | console.debug(`Matched exact locale ${requestedLocale}`); 45 | return exactMatch; 46 | } 47 | 48 | let localeParts = requestedLocale.split('-'); 49 | if (localeParts.length !== 2) { 50 | console.debug( 51 | `Found no matching locale for ${requestedLocale}, falling back to en`, 52 | ); 53 | return 'en'; 54 | } 55 | 56 | let baseLocale = localeParts[0]; 57 | let baseLocaleMatch = Object.keys(localeMessages).find( 58 | (locale) => baseLocale === locale, 59 | ); 60 | if (baseLocaleMatch) { 61 | console.debug(`Matched base locale ${baseLocale} for ${requestedLocale}`); 62 | return baseLocaleMatch; 63 | } 64 | 65 | console.debug( 66 | `Found no matching locale for ${requestedLocale}, falling back to en`, 67 | ); 68 | return 'en'; 69 | } 70 | const i18n = new VueI18n({ 71 | locale: 'en', 72 | fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', 73 | messages: localeMessages, 74 | }); 75 | 76 | getDefaultLanguage().then((defaultLanguage) => { 77 | const matchedLocale = matchLocale( 78 | (localStorage && localStorage.getItem('Locale')) || 79 | defaultLanguage || 80 | navigator.language || 81 | navigator.userLanguage, 82 | ); 83 | i18n.locale = matchedLocale; 84 | }); 85 | 86 | export default i18n; 87 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import 'core-js/stable'; 4 | import 'regenerator-runtime/runtime'; 5 | import Vue from 'vue'; 6 | import BootstrapVue from 'bootstrap-vue'; 7 | import App from './App'; 8 | import router from './router'; 9 | import i18n from './i18n'; 10 | import './validation'; 11 | import './plugins/table.js'; 12 | import axios from 'axios'; 13 | import VueAxios from 'vue-axios'; 14 | import vueDebounce from 'vue-debounce'; 15 | import VuePageTitle from 'vue-page-title'; 16 | import '@/directives/VuePermission'; 17 | import VueToastr from 'vue-toastr'; 18 | import api from './shared/api.json'; 19 | import oidc from './shared/oidc.json'; 20 | import version from './version'; 21 | import { getContextPath } from './shared/utils'; 22 | 23 | Vue.use(BootstrapVue); 24 | Vue.use(VueAxios, axios); 25 | Vue.use(VueToastr, { 26 | defaultTimeout: 5000, 27 | defaultProgressBar: false, 28 | defaultProgressBarValue: 0, 29 | defaultPosition: 'toast-top-right', 30 | defaultCloseOnHover: false, 31 | }); 32 | Vue.use(vueDebounce, { defaultTime: '750ms' }); 33 | Vue.use(VuePageTitle, { prefix: 'Dependency-Track -', router }); 34 | 35 | Vue.prototype.$api = api; 36 | Vue.prototype.$oidc = oidc; 37 | const contextPath = getContextPath(); 38 | axios 39 | .get(contextPath + '/static/config.json') 40 | .then((response) => { 41 | if (response.data.API_BASE_URL && response.data.API_BASE_URL !== '') { 42 | Vue.prototype.$api.BASE_URL = response.data.API_BASE_URL; 43 | } else { 44 | Vue.prototype.$api.BASE_URL = contextPath; 45 | } 46 | 47 | // Send XHR cross-site cookie credentials 48 | Vue.prototype.$api.WITH_CREDENTIALS = 49 | response.data.API_WITH_CREDENTIALS && 50 | response.data.API_WITH_CREDENTIALS.toLowerCase() === 'true'; 51 | 52 | // OpenID Connect 53 | Vue.prototype.$oidc.ISSUER = response.data.OIDC_ISSUER; 54 | Vue.prototype.$oidc.CLIENT_ID = response.data.OIDC_CLIENT_ID; 55 | Vue.prototype.$oidc.SCOPE = response.data.OIDC_SCOPE; 56 | Vue.prototype.$oidc.FLOW = response.data.OIDC_FLOW; 57 | if (response.data.OIDC_LOGIN_BUTTON_TEXT) { 58 | Vue.prototype.$oidc.LOGIN_BUTTON_TEXT = 59 | response.data.OIDC_LOGIN_BUTTON_TEXT; 60 | } else { 61 | Vue.prototype.$oidc.LOGIN_BUTTON_TEXT = ''; 62 | } 63 | createVueApp(); 64 | }) 65 | .catch(function (error) { 66 | console.log( 67 | 'Cannot retrieve static/config.json from host. This is expected behavior in development environments.', 68 | ); 69 | createVueApp(); 70 | }); 71 | 72 | /** 73 | * Removed finally block due to: 74 | * https://github.com/DependencyTrack/frontend/issues/34 75 | */ 76 | function createVueApp() { 77 | /* 78 | Register global $dtrack variable which will be the response body from /api/version. 79 | $dtrack can then be used anywhere in the app to get information about the server, 80 | the version of dtrack, timestamp, uuid, Alpine version, etc. 81 | */ 82 | axios 83 | .get(`${Vue.prototype.$api.BASE_URL}/${Vue.prototype.$api.URL_ABOUT}`) 84 | .then((result) => { 85 | Vue.prototype.$dtrack = result.data; 86 | }); 87 | 88 | Vue.prototype.$version = version; 89 | 90 | new Vue({ 91 | el: '#app', 92 | router, 93 | template: '', 94 | components: { 95 | App, 96 | }, 97 | i18n, 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /src/mixins/availableClassifiersMixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | availableClassifiers: [ 5 | { 6 | value: 'NONE', 7 | text: this.$i18n.t('message.component_none_classifier'), 8 | }, 9 | { 10 | value: 'APPLICATION', 11 | text: this.$i18n.t('message.component_application'), 12 | }, 13 | { 14 | value: 'FRAMEWORK', 15 | text: this.$i18n.t('message.component_framework'), 16 | }, 17 | { value: 'LIBRARY', text: this.$i18n.t('message.component_library') }, 18 | { 19 | value: 'CONTAINER', 20 | text: this.$i18n.t('message.component_container'), 21 | }, 22 | { 23 | value: 'OPERATING_SYSTEM', 24 | text: this.$i18n.t('message.component_operating_system'), 25 | }, 26 | { value: 'DEVICE', text: this.$i18n.t('message.component_device') }, 27 | { value: 'FIRMWARE', text: this.$i18n.t('message.component_firmware') }, 28 | { value: 'FILE', text: this.$i18n.t('message.component_file') }, 29 | { value: 'PLATFORM', text: this.$i18n.t('message.component_platform') }, 30 | { 31 | value: 'DEVICE_DRIVER', 32 | text: this.$i18n.t('message.component_device_driver'), 33 | }, 34 | { 35 | value: 'MACHINE_LEARNING_MODEL', 36 | text: this.$i18n.t('message.component_machine_learning_model'), 37 | }, 38 | { value: 'DATA', text: this.$i18n.t('message.component_data') }, 39 | ], 40 | }; 41 | }, 42 | computed: { 43 | sortAvailableClassifiers: function () { 44 | this.availableClassifiers.sort(function (a, b) { 45 | return a.value === 'NONE' ? -1 : a.text.localeCompare(b.text); 46 | }); 47 | return this.availableClassifiers; 48 | }, 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /src/mixins/availableCollectionLogicsMixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | availableCollectionLogics: [ 5 | { 6 | value: 'NONE', 7 | text: this.$i18n.t('message.project_collection_logic_none'), 8 | }, 9 | { 10 | value: 'AGGREGATE_DIRECT_CHILDREN', 11 | text: this.$i18n.t( 12 | 'message.project_collection_logic_aggregate_direct_children', 13 | ), 14 | }, 15 | { 16 | value: 'AGGREGATE_DIRECT_CHILDREN_WITH_TAG', 17 | text: this.$i18n.t( 18 | 'message.project_collection_logic_aggregate_direct_children_with_tag', 19 | ), 20 | }, 21 | { 22 | value: 'AGGREGATE_LATEST_VERSION_CHILDREN', 23 | text: this.$i18n.t( 24 | 'message.project_collection_logic_latest_version_children', 25 | ), 26 | }, 27 | ], 28 | }; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/mixins/bootstrapTableMixin.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | export default { 4 | data() { 5 | return { 6 | vueFormatters: [], 7 | }; 8 | }, 9 | methods: { 10 | vueFormatter(obj) { 11 | const key = `_vue_formatter_${this.vueFormatters.length}`; 12 | this.vueFormatters.push({ 13 | el: `.${key}`, 14 | name: key, 15 | ...obj, 16 | }); 17 | return `
`; 18 | }, 19 | vueFormatterInit() { 20 | if (!this.vueFormatters.length) { 21 | return; 22 | } 23 | for (let i = this.vueFormatters.length - 1; i >= 0; i--) { 24 | const formatter = this.vueFormatters[i]; 25 | if (document.getElementsByClassName(formatter.name)) { 26 | new Vue(formatter); 27 | this.vueFormatters.splice(i, 1); 28 | } 29 | } 30 | }, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /src/mixins/globalVarsMixin.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import axios from 'axios'; 3 | import EventBus from '@/shared/eventbus'; 4 | 5 | export default { 6 | data() { 7 | return { 8 | dtrack: Object, 9 | currentUser: Object, 10 | }; 11 | }, 12 | created() { 13 | if (this.$dtrack) { 14 | this.dtrack = this.$dtrack; 15 | } else { 16 | axios 17 | .get(`${Vue.prototype.$api.BASE_URL}/${Vue.prototype.$api.URL_ABOUT}`) 18 | .then((result) => { 19 | this.dtrack = result.data; 20 | }); 21 | } 22 | if (this.$currentUser) { 23 | this.currentUser = this.$currentUser; 24 | } else { 25 | EventBus.$emit('profileUpdated'); 26 | } 27 | }, 28 | mounted() { 29 | EventBus.$on('profileUpdated', () => { 30 | axios 31 | .get( 32 | `${Vue.prototype.$api.BASE_URL}/${Vue.prototype.$api.URL_USER_SELF}`, 33 | ) 34 | .then((result) => { 35 | this.currentUser = result.data; 36 | }); 37 | }); 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/mixins/permissionsMixin.js: -------------------------------------------------------------------------------- 1 | import * as permissions from '../shared/permissions'; 2 | 3 | export default { 4 | data() { 5 | return { 6 | PERMISSIONS: { 7 | BOM_UPLOAD: permissions.BOM_UPLOAD, 8 | VIEW_PORTFOLIO: permissions.VIEW_PORTFOLIO, 9 | PORTFOLIO_MANAGEMENT: permissions.PORTFOLIO_MANAGEMENT, 10 | ACCESS_MANAGEMENT: permissions.ACCESS_MANAGEMENT, 11 | VIEW_VULNERABILITY: permissions.VIEW_VULNERABILITY, 12 | VULNERABILITY_ANALYSIS: permissions.VULNERABILITY_ANALYSIS, 13 | VIEW_POLICY_VIOLATION: permissions.VIEW_POLICY_VIOLATION, 14 | VULNERABILITY_MANAGEMENT: permissions.VULNERABILITY_MANAGEMENT, 15 | POLICY_VIOLATION_ANALYSIS: permissions.POLICY_VIOLATION_ANALYSIS, 16 | SYSTEM_CONFIGURATION: permissions.SYSTEM_CONFIGURATION, 17 | POLICY_MANAGEMENT: permissions.POLICY_MANAGEMENT, 18 | TAG_MANAGEMENT: permissions.TAG_MANAGEMENT, 19 | }, 20 | }; 21 | }, 22 | computed: { 23 | decodedToken() { 24 | return permissions.decodeToken(permissions.getToken()); 25 | }, 26 | }, 27 | methods: { 28 | isPermitted(permission) { 29 | return permissions.hasPermission(permission, this.decodedToken); 30 | }, 31 | isNotPermitted(permission) { 32 | return !permissions.hasPermission(permission, this.decodedToken); 33 | }, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /src/mixins/routerMixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | setSearchTextQuery(searchText) { 4 | let queries = JSON.parse(JSON.stringify(this.$route.query)); 5 | if (searchText === '') { 6 | delete queries.searchText; 7 | } else { 8 | queries.searchText = searchText; 9 | } 10 | this.$router.replace({ query: queries }); 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/plugins/jquery.js: -------------------------------------------------------------------------------- 1 | import jQuery from 'jquery'; 2 | window.jQuery = jQuery; 3 | -------------------------------------------------------------------------------- /src/plugins/table.js: -------------------------------------------------------------------------------- 1 | import 'bootstrap-table/dist/bootstrap-table.min.css'; 2 | 3 | import './jquery.js'; 4 | import Vue from 'vue'; 5 | import 'bootstrap'; 6 | import 'jquery-treegrid/js/jquery.treegrid.min.js'; 7 | import 'bootstrap-table/dist/bootstrap-table.js'; 8 | import 'bootstrap-table/dist/extensions/treegrid/bootstrap-table-treegrid.min.js'; 9 | import 'bootstrap-table/dist/extensions/defer-url/bootstrap-table-defer-url.js'; 10 | import BootstrapTable from 'bootstrap-table/dist/bootstrap-table-vue.esm.js'; 11 | 12 | Vue.component('BootstrapTable', BootstrapTable); 13 | -------------------------------------------------------------------------------- /src/shared/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "BASE_URL": "", 3 | "CONTENT_TYPE_JSON": "application/json", 4 | "CONTENT_TYPE_TEXT": "text/plain", 5 | "DATA_TYPE": "json", 6 | "ENCODE_MULTIPART_FORM_DATA": "multipart/form-data", 7 | "FORCE_PASSWORD_CHANGE": "FORCE_PASSWORD_CHANGE", 8 | "METHOD_DELETE": "DELETE", 9 | "METHOD_GET": "GET", 10 | "METHOD_POST": "POST", 11 | "METHOD_PUT": "PUT", 12 | "TOTAL_COUNT_HEADER": "X-Total-Count", 13 | "URL_ABOUT": "api/version", 14 | "URL_ACL_MAPPING": "api/v1/acl/mapping", 15 | "URL_ACL_TEAM": "api/v1/acl/team", 16 | "URL_ANALYSIS": "api/v1/analysis", 17 | "URL_BOM": "api/v1/bom", 18 | "URL_CALCULATOR_CVSS": "api/v1/calculator/cvss", 19 | "URL_CALCULATOR_OWASP": "api/v1/calculator/owasp", 20 | "URL_COMPONENT": "api/v1/component", 21 | "URL_CONFIG_PROPERTY": "api/v1/configProperty", 22 | "URL_CWE": "api/v1/cwe", 23 | "URL_DEPENDENCY_GRAPH": "api/v1/dependencyGraph", 24 | "URL_FINDING": "api/v1/finding", 25 | "URL_FORCE_PW_CHANGE": "api/v1/user/forceChangePassword", 26 | "URL_LDAP_GROUPS": "api/v1/ldap/groups", 27 | "URL_LDAP_MAPPING": "api/v1/ldap/mapping", 28 | "URL_LICENSE": "api/v1/license", 29 | "URL_LICENSE_CONCISE": "api/v1/license/concise", 30 | "URL_LICENSE_GROUP": "api/v1/licenseGroup", 31 | "URL_LOGIN": "api/v1/user/login", 32 | "URL_METRICS": "api/v1/metrics", 33 | "URL_NOTIFICATION_PUBLISHER": "api/v1/notification/publisher", 34 | "URL_NOTIFICATION_RULE": "api/v1/notification/rule", 35 | "URL_OIDC_AVAILABLE": "api/v1/oidc/available", 36 | "URL_OIDC_GROUP": "api/v1/oidc/group", 37 | "URL_OIDC_MAPPING": "api/v1/oidc/mapping", 38 | "URL_OSV_ECOSYSTEM": "api/v1/integration/osv/ecosystem", 39 | "URL_PERMISSION": "api/v1/permission", 40 | "URL_POLICY": "api/v1/policy", 41 | "URL_POLICY_VIOLATION": "api/v1/violation", 42 | "URL_POLICY_VIOLATION_ANALYSIS": "api/v1/violation/analysis", 43 | "URL_PROJECT": "api/v1/project", 44 | "URL_REPOSITORY": "api/v1/repository", 45 | "URL_SEARCH": "api/v1/search", 46 | "URL_SERVICE": "api/v1/service", 47 | "URL_TAG": "api/v1/tag", 48 | "URL_TEAM": "api/v1/team", 49 | "URL_USER": "api/v1/user", 50 | "URL_USER_LDAP": "api/v1/user/ldap", 51 | "URL_USER_MANAGED": "api/v1/user/managed", 52 | "URL_USER_OIDC": "api/v1/user/oidc", 53 | "URL_USER_OIDC_LOGIN": "api/v1/user/oidc/login", 54 | "URL_USER_SELF": "api/v1/user/self", 55 | "URL_VEX": "api/v1/vex", 56 | "URL_VULNERABILITY": "api/v1/vulnerability", 57 | "WITH_CREDENTIALS": "" 58 | } 59 | -------------------------------------------------------------------------------- /src/shared/classes.js: -------------------------------------------------------------------------------- 1 | export const sidebarCssClasses = [ 2 | 'sidebar-show', 3 | 'sidebar-sm-show', 4 | 'sidebar-md-show', 5 | 'sidebar-lg-show', 6 | 'sidebar-xl-show', 7 | ]; 8 | 9 | export const asideMenuCssClasses = [ 10 | 'aside-menu-show', 11 | 'aside-menu-sm-show', 12 | 'aside-menu-md-show', 13 | 'aside-menu-lg-show', 14 | 'aside-menu-xl-show', 15 | ]; 16 | 17 | export const validBreakpoints = ['sm', 'md', 'lg', 'xl']; 18 | 19 | export function checkBreakpoint(breakpoint, list) { 20 | return list.indexOf(breakpoint) > -1; 21 | } 22 | -------------------------------------------------------------------------------- /src/shared/eventbus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | const EventBus = new Vue(); 3 | export default EventBus; 4 | -------------------------------------------------------------------------------- /src/shared/oidc.json: -------------------------------------------------------------------------------- 1 | { 2 | "CLIENT_ID": "", 3 | "FLOW": "", 4 | "ISSUER": "", 5 | "LOGIN_BUTTON_TEXT": "", 6 | "SCOPE": "" 7 | } 8 | -------------------------------------------------------------------------------- /src/shared/permissions.js: -------------------------------------------------------------------------------- 1 | // API Permissions 2 | export const BOM_UPLOAD = 'BOM_UPLOAD'; 3 | export const VIEW_PORTFOLIO = 'VIEW_PORTFOLIO'; 4 | export const PORTFOLIO_MANAGEMENT = 'PORTFOLIO_MANAGEMENT'; 5 | export const ACCESS_MANAGEMENT = 'ACCESS_MANAGEMENT'; 6 | export const VIEW_VULNERABILITY = 'VIEW_VULNERABILITY'; 7 | export const VULNERABILITY_ANALYSIS = 'VULNERABILITY_ANALYSIS'; 8 | export const VIEW_POLICY_VIOLATION = 'VIEW_POLICY_VIOLATION'; 9 | export const VULNERABILITY_MANAGEMENT = 'VULNERABILITY_MANAGEMENT'; 10 | export const POLICY_VIOLATION_ANALYSIS = 'POLICY_VIOLATION_ANALYSIS'; 11 | export const SYSTEM_CONFIGURATION = 'SYSTEM_CONFIGURATION'; 12 | export const POLICY_MANAGEMENT = 'POLICY_MANAGEMENT'; 13 | export const TAG_MANAGEMENT = 'TAG_MANAGEMENT'; 14 | 15 | /** 16 | * Determines if the current logged in user has a specific permission. 17 | * If the decodedToken is not passed, the function will automatically 18 | * retrieve and decode it. 19 | */ 20 | export const hasPermission = function hasPermission(permission, decodedToken) { 21 | const token = decodedToken || decodeToken(getToken()); 22 | const permissions = token?.permissions?.split(',') || []; 23 | return permissions.includes(permission); 24 | }; 25 | 26 | /** 27 | * Returns the decoded token as a JSON object. 28 | */ 29 | export const decodeToken = function decodeToken(token) { 30 | let base64Url = token.split('.')[1]; 31 | let base64 = base64Url.replace('-', '+').replace('_', '/'); 32 | return JSON.parse(window.atob(base64)); 33 | }; 34 | 35 | /** 36 | * Retrieves the token from session storage. 37 | */ 38 | export const getToken = function getToken() { 39 | return sessionStorage.getItem('token'); 40 | }; 41 | -------------------------------------------------------------------------------- /src/shared/toggle-classes.js: -------------------------------------------------------------------------------- 1 | export default function toggleClasses(toggleClass, classList, force) { 2 | const level = classList.indexOf(toggleClass); 3 | const removeClassList = classList.slice(0, level); 4 | removeClassList.map((className) => document.body.classList.remove(className)); 5 | document.body.classList.toggle(toggleClass, force); 6 | } 7 | -------------------------------------------------------------------------------- /src/validation/index.js: -------------------------------------------------------------------------------- 1 | import { extend, configure } from 'vee-validate'; 2 | import { 3 | required, 4 | confirmed, 5 | min_value, 6 | max_value, 7 | } from 'vee-validate/dist/rules'; 8 | 9 | import i18n from '../i18n'; 10 | 11 | // Get rule localization based on the rule name 12 | configure({ 13 | defaultMessage: (_, values) => i18n.t(`validation.${values._rule_}`, values), 14 | }); 15 | 16 | extend('required', required); 17 | extend('confirmed', confirmed); 18 | extend('min_value', min_value); 19 | extend('max_value', max_value); 20 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/ChangePasswordModal.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 108 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/CreateLdapUserModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 71 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/CreateOidcGroupModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 71 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/CreateOidcUserModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 71 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/CreateTeamModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 71 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/EditApiKeyCommentModal.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 79 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/Permissions.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 68 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/SelectLdapGroupModal.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 92 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/SelectOidcGroupModal.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 88 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/SelectPermissionModal.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 82 | -------------------------------------------------------------------------------- /src/views/administration/accessmanagement/SelectTeamModal.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 82 | -------------------------------------------------------------------------------- /src/views/administration/analyzers/VulnDbAnalyzer.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 110 | -------------------------------------------------------------------------------- /src/views/administration/configuration/EmailTestConfigurationModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 71 | -------------------------------------------------------------------------------- /src/views/administration/configuration/Experimental.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 52 | -------------------------------------------------------------------------------- /src/views/administration/configuration/InternalComponents.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 105 | -------------------------------------------------------------------------------- /src/views/administration/configuration/WelcomeMessage.vue: -------------------------------------------------------------------------------- 1 |