From 23ff4e385ca8802c7021785b6cd8ffc23de6674f Mon Sep 17 00:00:00 2001 From: Min Kim Date: Mon, 24 Feb 2025 18:50:06 -0500 Subject: [PATCH 01/79] Add example entities --- app-config.yaml | 6 ++ catalog-info.yaml | 156 +++++++++++++++++++++++++++++++++++++++++++++- yarn.lock | 2 +- 3 files changed, 160 insertions(+), 4 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 1386382d..49992f6d 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -114,3 +114,9 @@ catalog: schedule: frequency: { minutes: 60 } timeout: { minutes: 15 } + rules: + - allow: + [Component, API, Location, Template, Resource, System, Group, Domain] + locations: + - type: file + target: ../../catalog-info.yaml diff --git a/catalog-info.yaml b/catalog-info.yaml index 3d6fcb6c..4aa66ba9 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -1,13 +1,163 @@ apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: digital-office + title: Digital Office + description: OCIO Digital Office +spec: + type: division + children: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: Domain +metadata: + name: ecosystem + title: Digital Ecosystem + description: BC Government Digital Ecosystem +spec: + owner: group:digital-office +--- +apiVersion: backstage.io/v1alpha1 +kind: Domain +metadata: + name: ecosystem-enablers + title: Ecosystem Enablers + description: Internal services developed or governed by the BC Government for the benefit of the BC Government Digital Ecosystem +spec: + owner: group:digital-office + subdomainOf: ecosystem +--- +apiVersion: backstage.io/v1alpha1 +kind: Domain +metadata: + name: digital-services + title: Digital Services + description: External Digital Services developed by the BC Government +spec: + owner: group:digital-office + subdomainOf: ecosystem +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: silver + description: BC Gov silver-tier shared OpenShift/Kubernetes cluster. +spec: + type: kubernetes-cluster + owner: group:bcgov/platform-services-team +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: gold + description: BC Gov gold-tier shared OpenShift/Kubernetes cluster. +spec: + type: kubernetes-cluster + owner: group:bcgov/platform-services-team +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: gold-dr + description: BC Gov gold-tier shared disaster recovery OpenShift/Kubernetes cluster. +spec: + type: kubernetes-cluster + owner: group:bcgov/platform-services-team +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: emerald + description: BC Gov emerald-tier shared OpenShift/Kubernetes cluster. +spec: + type: kubernetes-cluster + owner: group:bcgov/platform-services-team +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: f5ff48-tools + description: Stores DevHub catalog and other related data. +spec: + type: kubernetes-namespace + owner: group:bcgov/exchange-lab-developer-portal-team + system: devhub +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: f5ff48-dev + description: Stores DevHub catalog and other related data. +spec: + type: kubernetes-namespace + owner: group:bcgov/exchange-lab-developer-portal-team + system: devhub +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: f5ff48-test + description: Stores DevHub catalog and other related data. +spec: + type: kubernetes-namespace + owner: group:bcgov/exchange-lab-developer-portal-team + system: devhub +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: f5ff48-prod + description: Stores DevHub catalog and other related data. +spec: + type: kubernetes-namespace + owner: group:bcgov/exchange-lab-developer-portal-team + system: devhub +--- +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + name: devhub + description: A system to help developers be more informed and efficient when building software for the BC Government. +spec: + owner: group:bcgov/developer-experience + lifecycle: production + domain: ecosystem-enablers +--- +apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: developer-portal description: A portal for the BC Government developer community. annotations: github.com/project-slug: bcgov/developer-portal - github.com/team-slug: bcgov/teams/exchange-lab-developer-portal-team - # backstage.io/techdocs-ref: dir:. + backstage.io/kubernetes-id: developer-portal spec: + system: devhub type: website owner: group:bcgov/exchange-lab-developer-portal-team - lifecycle: experimental + lifecycle: production + dependsOn: + - resource:devhub-db + - resource:devhub-techdocs-bucket +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: devhub-db + description: Stores DevHub catalog and other related data. + annotations: + backstage.io/kubernetes-id: devhub-db +spec: + type: database + owner: group:bcgov/exchange-lab-developer-portal-team + system: devhub +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: devhub-techdocs-bucket + description: Stores DevHub catalog and other related data. +spec: + type: s3-bucket + owner: group:bcgov/exchange-lab-developer-portal-team + system: devhub diff --git a/yarn.lock b/yarn.lock index 9ce595bd..10270bd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22999,7 +22999,7 @@ react-window@^1.8.10, react-window@^1.8.6: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react@^18.0.2: +"react@^16.13.1 || ^17.0.0 || ^18.0.0", react@^18.0.2: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== From d9d8c8c0b5407f47d35be11044646f7e6a44d612 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 24 Feb 2025 20:42:10 -0500 Subject: [PATCH 02/79] Added Policy kind with processor --- examples/policies-generated.yaml | 54 ++++++++++++ examples/policies.yaml | 29 ++++++ packages/backend/package.json | 1 + .../extensions/policyProcessorExtension.ts | 31 +++++++ packages/backend/src/index.ts | 1 + yarn.lock | 88 ++++++++++++++++++- 6 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 examples/policies-generated.yaml create mode 100644 examples/policies.yaml create mode 100644 packages/backend/src/extensions/policyProcessorExtension.ts diff --git a/examples/policies-generated.yaml b/examples/policies-generated.yaml new file mode 100644 index 00000000..b8ef972d --- /dev/null +++ b/examples/policies-generated.yaml @@ -0,0 +1,54 @@ +# geneated with prompt: turn these into backstage component entities of kind "policy" and apiVersion: bc-gov/policyv1 +apiVersion: bc-gov/policyv1 +kind: Policy +metadata: + name: dependency-analysis + description: Components perform dependency chain analysis +spec: + type: best-practice + level: optional +--- +apiVersion: bc-gov/policyv1 +kind: Policy +metadata: + name: typescript-strict + description: TypeScript projects configured to use strict mode +spec: + type: code-quality + level: recommended +--- +apiVersion: bc-gov/policyv1 +kind: Policy +metadata: + name: container-vulnerability + description: Services have container vulnerability scanning reports that are less than 90 days old. +spec: + type: security + level: required +--- +apiVersion: bc-gov/policyv1 +kind: Policy +metadata: + name: automatic-updates + description: Components configured to automatically update dependencies +spec: + type: best-practice + level: optional +--- +apiVersion: bc-gov/policyv1 +kind: Policy +metadata: + name: critical-dependencies + description: Components do not have CRITICAL alerts older than 30 days +spec: + type: security + level: required +--- +apiVersion: bc-gov/policyv1 +kind: Policy +metadata: + name: unencrypted-credentials + description: Components do not store unencrypted credentials in the repository +spec: + type: security + level: strictly-enforced diff --git a/examples/policies.yaml b/examples/policies.yaml new file mode 100644 index 00000000..b2e3d961 --- /dev/null +++ b/examples/policies.yaml @@ -0,0 +1,29 @@ +name: Dependency Analysis +type: best-practice +level: optional +description: Components perform dependency chain analysis +--- +name: TypeScript Strict mode +type: code-quality +level: recommended +description: TypeScript projects configured to use strict mode +--- +name: Container Vulnerability +type: security +level: required +description: Services have container vulnerability scanning reports that are less than 90 days old. +--- +name: Automatic Updates +type: best-practice +level: optional +description: Components configured to automatically update dependencies +--- +name: Critical dependencies +type: security +level: required +description: Components do not have CRITICAL alerts older than 30 days +--- +name: Unencrypted Credentials +type: security +level: strictly-enforced +description: Components do not store unencrypted credentials in the repository diff --git a/packages/backend/package.json b/packages/backend/package.json index a5720dcc..0849a951 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -33,6 +33,7 @@ "@backstage/plugin-catalog-backend-module-github-org": "^0.3.3", "@backstage/plugin-catalog-backend-module-logs": "^0.1.3", "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.1", + "@backstage/plugin-catalog-node": "^1.16.0", "@backstage/plugin-catalog-common": "^1.1.0", "@backstage/plugin-events-backend": "^0.3.15", "@backstage/plugin-permission-backend": "^0.5.50", diff --git a/packages/backend/src/extensions/policyProcessorExtension.ts b/packages/backend/src/extensions/policyProcessorExtension.ts new file mode 100644 index 00000000..0c6807f7 --- /dev/null +++ b/packages/backend/src/extensions/policyProcessorExtension.ts @@ -0,0 +1,31 @@ +import { Entity } from '@backstage/catalog-model'; +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; + +class PolicyEntityProcessor implements CatalogProcessor { + getProcessorName(): string { + return 'PolicyEntityProcessor'; + } + + async validateEntityKind(entity: Entity): Promise { + return entity.kind === 'Policy'; + } +} + +export const catalogModulePolicyProcessor = createBackendModule({ + pluginId: 'catalog', + moduleId: 'example-custom-processor', + register(env) { + env.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + }, + async init({ catalog }) { + catalog.addProcessor(new PolicyEntityProcessor()); + }, + }); + }, +}); + +export { catalogModulePolicyProcessor as default }; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 25d1f0dc..5c3002f6 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -27,6 +27,7 @@ backend.add(import('@backstage/plugin-techdocs-backend')); backend.add(import('@backstage/plugin-permission-backend')); backend.add(import('./extensions/permissionsPolicyExtension')); +backend.add(import('./extensions/policyProcessorExtension')); backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); backend.start(); diff --git a/yarn.lock b/yarn.lock index 10270bd2..b8347989 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2466,6 +2466,23 @@ knex "^3.0.0" luxon "^3.0.0" +"@backstage/backend-plugin-api@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@backstage/backend-plugin-api/-/backend-plugin-api-1.2.0.tgz#060e516f706d1201ece4379e2b49af181c346f58" + integrity sha512-dugL00Qq+Cu4AqcUfraPZhC8jo+Pg4YPx9sKm4XIf+27ZbPBGLJMKrOmCOYpZXBKMMBY44h9/oeg5fO3RnHetg== + dependencies: + "@backstage/cli-common" "^0.1.15" + "@backstage/config" "^1.3.2" + "@backstage/errors" "^1.2.7" + "@backstage/plugin-auth-node" "^0.6.0" + "@backstage/plugin-permission-common" "^0.8.4" + "@backstage/plugin-permission-node" "^0.8.8" + "@backstage/types" "^1.2.1" + "@types/express" "^4.17.6" + "@types/luxon" "^3.0.0" + knex "^3.0.0" + luxon "^3.0.0" + "@backstage/catalog-client@^1.7.1": version "1.7.1" resolved "https://registry.yarnpkg.com/@backstage/catalog-client/-/catalog-client-1.7.1.tgz#f85f4cfee20a25be556cc572e12f58e4b8cb1309" @@ -3413,6 +3430,27 @@ zod-to-json-schema "^3.21.4" zod-validation-error "^3.4.0" +"@backstage/plugin-auth-node@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@backstage/plugin-auth-node/-/plugin-auth-node-0.6.0.tgz#2e93811f4e72e145745b2d40d47e29c8f84bca1f" + integrity sha512-SiQ5H/ZCRkIi5dXDhfd71VvFl9UGW8MEpwe0G8B+5gCiiNGLKq31vHQS9jYZ2eI1aRqvsksOeVDgcN5aAE25Og== + dependencies: + "@backstage/backend-plugin-api" "^1.2.0" + "@backstage/catalog-client" "^1.9.1" + "@backstage/catalog-model" "^1.7.3" + "@backstage/config" "^1.3.2" + "@backstage/errors" "^1.2.7" + "@backstage/types" "^1.2.1" + "@types/express" "^4.17.6" + "@types/passport" "^1.0.3" + express "^4.17.1" + jose "^5.0.0" + lodash "^4.17.21" + passport "^0.7.0" + zod "^3.22.4" + zod-to-json-schema "^3.21.4" + zod-validation-error "^3.4.0" + "@backstage/plugin-auth-react@^0.1.7": version "0.1.7" resolved "https://registry.yarnpkg.com/@backstage/plugin-auth-react/-/plugin-auth-react-0.1.7.tgz#974d9fa7c687dfa0dd06252213c2881e631aaf06" @@ -3536,6 +3574,15 @@ "@backstage/plugin-permission-common" "^0.8.1" "@backstage/plugin-search-common" "^1.2.14" +"@backstage/plugin-catalog-common@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog-common/-/plugin-catalog-common-1.1.3.tgz#f611e0ed963af63cd6a20ef724fcd8537789267c" + integrity sha512-r0TVBLKbdI8nbrBAtm3PqXbJi4U6exjtjIH0AIbJFEWVkVmKkLDCPNnkaPZ/aIxK/nZ0WWMM6YN2fpNbFcSIeg== + dependencies: + "@backstage/catalog-model" "^1.7.3" + "@backstage/plugin-permission-common" "^0.8.4" + "@backstage/plugin-search-common" "^1.2.17" + "@backstage/plugin-catalog-graph@^0.4.11": version "0.4.11" resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog-graph/-/plugin-catalog-graph-0.4.11.tgz#c12e460d528ee6eb9dcb80419f26027c3ba7f5c9" @@ -3600,6 +3647,20 @@ "@backstage/plugin-permission-node" "^0.8.4" "@backstage/types" "^1.1.1" +"@backstage/plugin-catalog-node@^1.16.0": + version "1.16.0" + resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog-node/-/plugin-catalog-node-1.16.0.tgz#a8ed7bbb91043d576360a533a8a3147b2722ef88" + integrity sha512-Bwa8L634EFaqWJLarkOZNBAt6UNjCufyRPALcDNivB2QVvD5oz8P6hd3oeTE/rQBAAMHbcT2fTpXRtySbzlZpg== + dependencies: + "@backstage/backend-plugin-api" "^1.2.0" + "@backstage/catalog-client" "^1.9.1" + "@backstage/catalog-model" "^1.7.3" + "@backstage/errors" "^1.2.7" + "@backstage/plugin-catalog-common" "^1.1.3" + "@backstage/plugin-permission-common" "^0.8.4" + "@backstage/plugin-permission-node" "^0.8.8" + "@backstage/types" "^1.2.1" + "@backstage/plugin-catalog-react@^1.12.3", "@backstage/plugin-catalog-react@^1.14.0": version "1.14.0" resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog-react/-/plugin-catalog-react-1.14.0.tgz#d50b62ccc1fe6a0988cb8c89412c8e44e8a9bf5e" @@ -3828,6 +3889,23 @@ zod "^3.22.4" zod-to-json-schema "^3.20.4" +"@backstage/plugin-permission-node@^0.8.8": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@backstage/plugin-permission-node/-/plugin-permission-node-0.8.8.tgz#0f9f4a6f4a955f06cacac7aa728ca14a5d00710f" + integrity sha512-CCOjbKFyaZB81fxc/+HAzTsHWfY07XUb6KGNw4D0UlObMLfozfJX09/wvOEM51jDWo+E1YprZ/HJ9CafGfWvgg== + dependencies: + "@backstage/backend-common" "^0.25.0" + "@backstage/backend-plugin-api" "^1.2.0" + "@backstage/config" "^1.3.2" + "@backstage/errors" "^1.2.7" + "@backstage/plugin-auth-node" "^0.6.0" + "@backstage/plugin-permission-common" "^0.8.4" + "@types/express" "^4.17.6" + express "^4.17.1" + express-promise-router "^4.1.0" + zod "^3.22.4" + zod-to-json-schema "^3.20.4" + "@backstage/plugin-permission-react@^0.4.27": version "0.4.27" resolved "https://registry.yarnpkg.com/@backstage/plugin-permission-react/-/plugin-permission-react-0.4.27.tgz#d85a3b1d2bfefb740e5f666d0a0202980730ea4b" @@ -4329,6 +4407,14 @@ "@backstage/plugin-permission-common" "^0.8.1" "@backstage/types" "^1.1.1" +"@backstage/plugin-search-common@^1.2.17": + version "1.2.17" + resolved "https://registry.yarnpkg.com/@backstage/plugin-search-common/-/plugin-search-common-1.2.17.tgz#785db1d1bebae1ec22bc03ad4546df1124a848db" + integrity sha512-7wVfYCIFGI6JmTf/v43Odwdmyn3glngYa35pPRIJcFYEEmDyoeq3u9vR6A9dzpUtmKBjNJp6/3ILRYQmCkx1Pw== + dependencies: + "@backstage/plugin-permission-common" "^0.8.4" + "@backstage/types" "^1.2.1" + "@backstage/plugin-search-react@^1.8.1": version "1.8.1" resolved "https://registry.yarnpkg.com/@backstage/plugin-search-react/-/plugin-search-react-1.8.1.tgz#111499e6234ae7eb281dd402a459b64de36a415d" @@ -22999,7 +23085,7 @@ react-window@^1.8.10, react-window@^1.8.6: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -"react@^16.13.1 || ^17.0.0 || ^18.0.0", react@^18.0.2: +react@^18.0.2: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== From 31762d66b3d2239917a69d2b4f02ca6e5cfc112f Mon Sep 17 00:00:00 2001 From: Min Kim Date: Mon, 24 Feb 2025 20:55:05 -0500 Subject: [PATCH 03/79] Create header for security polices card --- .../app/src/components/catalog/EntityPage.tsx | 701 +++++++++--------- .../EntityPoliciesCard/EntityPoliciesCard.tsx | 80 ++ .../catalog/EntityPoliciesCard/index.ts | 1 + 3 files changed, 432 insertions(+), 350 deletions(-) create mode 100644 packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx create mode 100644 packages/app/src/components/catalog/EntityPoliciesCard/index.ts diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index b007441c..227b95d8 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -1,242 +1,243 @@ import React from 'react'; -import {Button, Grid} from '@material-ui/core'; +import { Button, Grid } from '@material-ui/core'; import { - EntityApiDefinitionCard, - EntityConsumedApisCard, - EntityConsumingComponentsCard, - EntityHasApisCard, - EntityProvidedApisCard, - EntityProvidingComponentsCard, + EntityApiDefinitionCard, + EntityConsumedApisCard, + EntityConsumingComponentsCard, + EntityHasApisCard, + EntityProvidedApisCard, + EntityProvidingComponentsCard, } from '@backstage/plugin-api-docs'; import { - EntityAboutCard, - EntityDependsOnComponentsCard, - EntityDependsOnResourcesCard, - EntityHasComponentsCard, - EntityHasResourcesCard, - EntityHasSubcomponentsCard, - EntityHasSystemsCard, - EntityLayout, - EntityLinksCard, - EntitySwitch, - EntityOrphanWarning, - EntityProcessingErrorsPanel, - isComponentType, - isKind, - hasCatalogProcessingErrors, - isOrphan, - hasRelationWarnings, - EntityRelationWarning, + EntityAboutCard, + EntityDependsOnComponentsCard, + EntityDependsOnResourcesCard, + EntityHasComponentsCard, + EntityHasResourcesCard, + EntityHasSystemsCard, + EntityLayout, + EntityLinksCard, + EntitySwitch, + EntityOrphanWarning, + EntityProcessingErrorsPanel, + isComponentType, + isKind, + hasCatalogProcessingErrors, + isOrphan, + hasRelationWarnings, + EntityRelationWarning, } from '@backstage/plugin-catalog'; import { - isGithubActionsAvailable, - EntityGithubActionsContent, + isGithubActionsAvailable, + EntityGithubActionsContent, } from '@backstage-community/plugin-github-actions'; import { - EntityUserProfileCard, - EntityGroupProfileCard, - EntityMembersListCard, - EntityOwnershipCard, + EntityUserProfileCard, + EntityGroupProfileCard, + EntityMembersListCard, + EntityOwnershipCard, } from '@backstage/plugin-org'; -import {EntityTechdocsContent} from '@backstage/plugin-techdocs'; -import {EmptyState} from '@backstage/core-components'; +import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; +import { EmptyState } from '@backstage/core-components'; import { - Direction, - EntityCatalogGraphCard, + Direction, + EntityCatalogGraphCard, } from '@backstage/plugin-catalog-graph'; import { - RELATION_API_CONSUMED_BY, - RELATION_API_PROVIDED_BY, - RELATION_CONSUMES_API, - RELATION_DEPENDENCY_OF, - RELATION_DEPENDS_ON, - RELATION_HAS_PART, - RELATION_PART_OF, - RELATION_PROVIDES_API, + RELATION_API_CONSUMED_BY, + RELATION_API_PROVIDED_BY, + RELATION_CONSUMES_API, + RELATION_DEPENDENCY_OF, + RELATION_DEPENDS_ON, + RELATION_HAS_PART, + RELATION_PART_OF, + RELATION_PROVIDES_API, } from '@backstage/catalog-model'; -import {TechDocsAddons} from '@backstage/plugin-techdocs-react'; -import {ReportIssue} from '@backstage/plugin-techdocs-module-addons-contrib'; -import {Mermaid} from "backstage-plugin-techdocs-addon-mermaid"; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; +import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; +import { Mermaid } from 'backstage-plugin-techdocs-addon-mermaid'; import { EntitySecurityInsightsContent } from '@roadiehq/backstage-plugin-security-insights'; - +import { EntityPoliciesCard } from './EntityPoliciesCard'; const techdocsContent = ( - - - - - - + + + + + + ); const cicdContent = ( - // This is an example of how you can implement your company's logic in entity page. - // You can for example enforce that all components of type 'service' should use GitHubActions - - - - - - - - Read more - - } - /> - - + // This is an example of how you can implement your company's logic in entity page. + // You can for example enforce that all components of type 'service' should use GitHubActions + + + + + + + + Read more + + } + /> + + ); const entityWarningContent = ( - <> - - - - - - - - - - - - - - - - - - - - - - - - + <> + + + + + + + + + + + + + + + + + + + + + + + + ); const overviewContent = ( - - {entityWarningContent} - - - - - - - - + + {entityWarningContent} + + + + + + + + {/* - - + */} + + + + + ); const serviceEntityPage = ( - - - {overviewContent} - - - - {cicdContent} - - - - - - - - - - - - - - - - - - - - - - - - - - {techdocsContent} - - - - - - + + + {overviewContent} + + + + {cicdContent} + + + + + + + + + + + + + + + + + + + + + + + + + + {techdocsContent} + + + + + + ); const websiteEntityPage = ( - - - {overviewContent} - - - - {cicdContent} - - - - - - - - - - - - - - - {techdocsContent} - - - - - - + + + {overviewContent} + + + + {cicdContent} + + + + + + + + + + + + + + + {techdocsContent} + + + + + + ); const documentationEntityPage = ( - - - {overviewContent} - - - - {cicdContent} - - - - {techdocsContent} - - + + + {overviewContent} + + + + {cicdContent} + + + + {techdocsContent} + + ); /** @@ -247,180 +248,180 @@ const documentationEntityPage = ( */ const defaultEntityPage = ( - - - {overviewContent} - - - - {techdocsContent} - - + + + {overviewContent} + + + + {techdocsContent} + + ); const componentPage = ( - - - {serviceEntityPage} - + + + {serviceEntityPage} + - - {websiteEntityPage} - + + {websiteEntityPage} + - - {documentationEntityPage} - + + {documentationEntityPage} + - {defaultEntityPage} - + {defaultEntityPage} + ); const apiPage = ( - - - - {entityWarningContent} - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + {entityWarningContent} + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); const userPage = ( - - - - {entityWarningContent} - - - - - - - - - + + + + {entityWarningContent} + + + + + + + + + ); const groupPage = ( - - - - {entityWarningContent} - - - - - - - - - - - - + + + + {entityWarningContent} + + + + + + + + + + + + ); const systemPage = ( - - - - {entityWarningContent} - - - - - - - - - - - - - - - - - - - - - - - - + + + + {entityWarningContent} + + + + + + + + + + + + + + + + + + + + + + + + ); const domainPage = ( - - - - {entityWarningContent} - - - - - - - - - - - - + + + + {entityWarningContent} + + + + + + + + + + + + ); export const entityPage = ( - - - - - - - - - {defaultEntityPage} - + + + + + + + + + {defaultEntityPage} + ); diff --git a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx new file mode 100644 index 00000000..988855f4 --- /dev/null +++ b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import Card from '@material-ui/core/Card'; +import Chip from '@material-ui/core/Chip'; +import CardHeader from '@material-ui/core/CardHeader'; +import Divider from '@material-ui/core/Divider'; +import CardContent from '@material-ui/core/CardContent'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import { InfoCardVariants } from '@backstage/core-components'; +import { makeStyles } from '@material-ui/core/styles'; +import { useEntity } from '@backstage/plugin-catalog-react'; + +interface PoliciesCardProps { + variant?: InfoCardVariants; +} + +const useStyles = makeStyles({ + header: { + display: 'flex', + justifyContent: 'space-between', + marginBottom: '15px', + }, + headerRating: { + display: 'flex', + alignItems: 'center', + }, + goldRating: { + backgroundColor: 'gold', + border: '3px solid orange', + borderRadius: '10px', + padding: '5px', + marginLeft: '10px', + color: 'gray', + }, + gridItemCard: { + display: 'flex', + flexDirection: 'column', + height: 'calc(100% - 10px)', + marginBottom: '10px', + }, + fullHeightCard: { + display: 'flex', + flexDirection: 'column', + height: '100%', + }, + cardContent: { + flex: 1, + }, +}); + +export function EntityPoliciesCard(props: PoliciesCardProps) { + const { variant } = props; + const classes = useStyles(); + const { entity } = useEntity(); + + let cardClass = ''; + if (variant === 'gridItem') { + cardClass = classes.gridItemCard; + } else if (variant === 'fullHeight') { + cardClass = classes.fullHeightCard; + } + + return ( + + + Security Policies + + Security Rating + + + + } + /> + + + + ); +} diff --git a/packages/app/src/components/catalog/EntityPoliciesCard/index.ts b/packages/app/src/components/catalog/EntityPoliciesCard/index.ts new file mode 100644 index 00000000..bd9f846b --- /dev/null +++ b/packages/app/src/components/catalog/EntityPoliciesCard/index.ts @@ -0,0 +1 @@ +export { EntityPoliciesCard } from './EntityPoliciesCard'; From d432f77732fe91042769835e9623bca5abbe98b3 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Mon, 24 Feb 2025 21:31:27 -0500 Subject: [PATCH 04/79] Update example entities --- catalog-info.yaml | 100 +++++++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/catalog-info.yaml b/catalog-info.yaml index 4aa66ba9..f802bf88 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -76,88 +76,116 @@ spec: apiVersion: backstage.io/v1alpha1 kind: Resource metadata: - name: f5ff48-tools - description: Stores DevHub catalog and other related data. + name: xyz123-tools + description: For deployment of tools and utilities related to live apps. spec: type: kubernetes-namespace - owner: group:bcgov/exchange-lab-developer-portal-team - system: devhub + owner: group:bcgov/bc-parks-reservation + system: bcparks-reservation --- apiVersion: backstage.io/v1alpha1 kind: Resource metadata: - name: f5ff48-dev - description: Stores DevHub catalog and other related data. + name: xyz123-dev + description: For deployment of development application artifacts. spec: type: kubernetes-namespace - owner: group:bcgov/exchange-lab-developer-portal-team - system: devhub + owner: group:bcgov/bc-parks-reservation + system: bcparks-reservation --- apiVersion: backstage.io/v1alpha1 kind: Resource metadata: - name: f5ff48-test - description: Stores DevHub catalog and other related data. + name: xyz123-test + description: For deployment of test application artifacts. spec: type: kubernetes-namespace - owner: group:bcgov/exchange-lab-developer-portal-team - system: devhub + owner: group:bcgov/bc-parks-reservation + system: bcparks-reservation --- apiVersion: backstage.io/v1alpha1 kind: Resource metadata: - name: f5ff48-prod - description: Stores DevHub catalog and other related data. + name: xyz123-prod + description: For deployment of production application artifacts. spec: type: kubernetes-namespace - owner: group:bcgov/exchange-lab-developer-portal-team - system: devhub + owner: group:bcgov/bc-parks-reservation + system: bcparks-reservation --- apiVersion: backstage.io/v1alpha1 kind: System metadata: - name: devhub - description: A system to help developers be more informed and efficient when building software for the BC Government. + name: bcparks-reservation + description: BC Parks reservation system. spec: owner: group:bcgov/developer-experience lifecycle: production - domain: ecosystem-enablers + domain: digital-services --- apiVersion: backstage.io/v1alpha1 kind: Component metadata: - name: developer-portal - description: A portal for the BC Government developer community. + name: reserve-rec-public + description: For the Parks and Recreation Digital Transformation project. annotations: - github.com/project-slug: bcgov/developer-portal - backstage.io/kubernetes-id: developer-portal + github.com/project-slug: bcgov/reserve-rec-public spec: - system: devhub + system: bcparks-reservation type: website - owner: group:bcgov/exchange-lab-developer-portal-team + owner: group:bcgov/bc-parks-reservation lifecycle: production dependsOn: - - resource:devhub-db + - component:reserve-rec-api - resource:devhub-techdocs-bucket --- apiVersion: backstage.io/v1alpha1 -kind: Resource +kind: Component +metadata: + name: reserve-rec-api + description: For the Parks and Recreation Digital Transformation project. + annotations: + github.com/project-slug: bcgov/reserve-rec-api +spec: + system: bcparks-reservation + type: service + owner: group:bcgov/bc-parks-reservation + lifecycle: production + dependsOn: + - resource:reserve-rec-db +--- +apiVersion: backstage.io/v1alpha1 +kind: Component metadata: - name: devhub-db - description: Stores DevHub catalog and other related data. + name: reserve-rec-admin + description: For the Parks and Recreation Digital Transformation project. annotations: - backstage.io/kubernetes-id: devhub-db + github.com/project-slug: bcgov/reserve-rec-admin +spec: + system: bcparks-reservation + type: website + owner: group:bcgov/bc-parks-reservation + lifecycle: production + dependsOn: + - component:reserve-rec-api + - resource:devhub-techdocs-bucket +--- +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: reserve-rec-db + description: Data store for BC Parks reservation system. spec: type: database - owner: group:bcgov/exchange-lab-developer-portal-team - system: devhub + owner: group:bcgov/bc-parks-reservation + system: bcparks-reservation --- apiVersion: backstage.io/v1alpha1 kind: Resource metadata: - name: devhub-techdocs-bucket - description: Stores DevHub catalog and other related data. + name: reserve-rec-bucket + description: Stores documents and other related assets related to support the BC Parks Reservation system. spec: type: s3-bucket - owner: group:bcgov/exchange-lab-developer-portal-team - system: devhub + owner: group:bcgov/bc-parks-reservation + system: bcparks-reservation From 4d4b1d7f6576c3eea088e70a25b38d53459f9c25 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 24 Feb 2025 22:20:20 -0500 Subject: [PATCH 05/79] Styled policies page --- examples/policies-generated.yaml | 7 +- examples/policies.yaml | 18 +++-- packages/app/package.json | 3 + packages/app/src/App.tsx | 3 +- packages/app/src/components/utils/columns.tsx | 65 +++++++++++++++++++ packages/backend/package.json | 1 + .../extensions/policyProcessorExtension.ts | 3 +- plugins/policy-common/.eslintrc.js | 1 + plugins/policy-common/README.md | 5 ++ plugins/policy-common/package.json | 37 +++++++++++ plugins/policy-common/src/index.ts | 1 + plugins/policy-common/src/models/policy.ts | 43 ++++++++++++ plugins/policy-common/src/setupTests.ts | 1 + yarn.lock | 19 ++++++ 14 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 packages/app/src/components/utils/columns.tsx create mode 100644 plugins/policy-common/.eslintrc.js create mode 100644 plugins/policy-common/README.md create mode 100644 plugins/policy-common/package.json create mode 100644 plugins/policy-common/src/index.ts create mode 100644 plugins/policy-common/src/models/policy.ts create mode 100644 plugins/policy-common/src/setupTests.ts diff --git a/examples/policies-generated.yaml b/examples/policies-generated.yaml index b8ef972d..017da70f 100644 --- a/examples/policies-generated.yaml +++ b/examples/policies-generated.yaml @@ -1,8 +1,8 @@ -# geneated with prompt: turn these into backstage component entities of kind "policy" and apiVersion: bc-gov/policyv1 apiVersion: bc-gov/policyv1 kind: Policy metadata: name: dependency-analysis + title: Dependency Chain Analysis description: Components perform dependency chain analysis spec: type: best-practice @@ -12,6 +12,7 @@ apiVersion: bc-gov/policyv1 kind: Policy metadata: name: typescript-strict + title: TypeScript Strict Mode description: TypeScript projects configured to use strict mode spec: type: code-quality @@ -21,6 +22,7 @@ apiVersion: bc-gov/policyv1 kind: Policy metadata: name: container-vulnerability + title: Container Vulnerability Scanning description: Services have container vulnerability scanning reports that are less than 90 days old. spec: type: security @@ -30,6 +32,7 @@ apiVersion: bc-gov/policyv1 kind: Policy metadata: name: automatic-updates + title: Automatic Dependency Updates description: Components configured to automatically update dependencies spec: type: best-practice @@ -39,6 +42,7 @@ apiVersion: bc-gov/policyv1 kind: Policy metadata: name: critical-dependencies + title: Critical Dependency Alerts description: Components do not have CRITICAL alerts older than 30 days spec: type: security @@ -48,6 +52,7 @@ apiVersion: bc-gov/policyv1 kind: Policy metadata: name: unencrypted-credentials + title: Credential Security description: Components do not store unencrypted credentials in the repository spec: type: security diff --git a/examples/policies.yaml b/examples/policies.yaml index b2e3d961..2b1dda3b 100644 --- a/examples/policies.yaml +++ b/examples/policies.yaml @@ -1,29 +1,35 @@ -name: Dependency Analysis +title: Dependency Analysis +name: dependency-analysis type: best-practice level: optional description: Components perform dependency chain analysis --- -name: TypeScript Strict mode +title: TypeScript Strict Mode +name: typescript-strict-mode type: code-quality level: recommended description: TypeScript projects configured to use strict mode --- -name: Container Vulnerability +title: Container Vulnerability +name: container-vulnerability type: security level: required description: Services have container vulnerability scanning reports that are less than 90 days old. --- -name: Automatic Updates +title: Automatic Updates +name: automatic-updates type: best-practice level: optional description: Components configured to automatically update dependencies --- -name: Critical dependencies +title: Critical dependencies +name: critical-dependencies type: security level: required description: Components do not have CRITICAL alerts older than 30 days --- -name: Unencrypted Credentials +title: Unencrypted Credentials +name: unencrypted-credentials type: security level: strictly-enforced description: Components do not store unencrypted credentials in the repository diff --git a/packages/app/package.json b/packages/app/package.json index 7277320f..eddf94b2 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -43,6 +43,7 @@ "@bcgov/bc-sans": "^2.0.0", "@bcgov/design-tokens": "^3.0.0", "@internal/plugin-analytics-module-snowplow": "^0.1.0", + "@internal/plugin-policy-common": "^0.1.0", "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.11.3", "@material-ui/lab": "^4.0.0-alpha.61", @@ -53,6 +54,7 @@ "@snowplow/browser-tracker": "^3.15.0", "backstage-plugin-techdocs-addon-mermaid": "^0.13.0", "history": "^5.0.0", + "lodash-es": "^4.17.21", "react": "^18.0.2", "react-dom": "^18.0.2", "react-router": "^6.3.0", @@ -70,6 +72,7 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.0.0", "@types/react-dom": "*", + "@types/lodash-es": "^4.17.12", "cross-env": "^7.0.0" }, "browserslist": { diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 60af5b33..1e1a6b8e 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -46,6 +46,7 @@ import { TocFix } from '@app/plugin-toc-fix2'; import { TechdocExpandableToc } from '@app/plugin-expandable-toc'; import { Mermaid } from 'backstage-plugin-techdocs-addon-mermaid'; import { Custom404Page } from './components/404/Custom404Page'; +import { columns } from './components/utils/columns'; const app = createApp({ apis, @@ -114,7 +115,7 @@ const routes = ( } /> - } /> + } /> } diff --git a/packages/app/src/components/utils/columns.tsx b/packages/app/src/components/utils/columns.tsx new file mode 100644 index 00000000..185b22ef --- /dev/null +++ b/packages/app/src/components/utils/columns.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { + CatalogTable, + CatalogTableColumnsFunc, +} from '@backstage/plugin-catalog'; +import { startCase, camelCase } from 'lodash-es'; +import { Theme, Typography, makeStyles } from '@material-ui/core'; + +function prettyText(text: string) { + return startCase(camelCase(text)); +} + +const useStyles = makeStyles((theme: Theme) => ({ + optional: { + color: theme.palette.success.main, + }, + recommended: { + color: theme.palette.primary.main, + }, + required: { + color: theme.palette.warning.main, + }, + 'strictly-enforced': { + color: theme.palette.error.main, + }, +})); + +export const columns: CatalogTableColumnsFunc = entityListContext => { + if (entityListContext.filters.kind?.value === 'policy') { + return [ + CatalogTable.columns.createNameColumn(), + { + title: 'Level', + field: 'spec.level', + width: '17%', + render: row => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const classes = useStyles(); + const level = + row.entity.spec?.level?.toString() as keyof typeof classes; + return ( + + {prettyText(level || '')} + + ); + }, + }, + { + title: 'Description', + field: 'resolved.description', + highlight: false, + render: ({ entity }) => entity.metadata?.description, + }, + { + title: 'Type', + field: 'spec.type', + render: row => { + return prettyText(row.entity.spec?.type?.toString() || ''); + }, + }, + ]; + } + + return CatalogTable.defaultColumnsFunc(entityListContext); +}; diff --git a/packages/backend/package.json b/packages/backend/package.json index 0849a951..6a17fbcb 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -50,6 +50,7 @@ "@backstage/plugin-search-backend-module-techdocs": "^0.3.1", "@backstage/plugin-search-backend-node": "^1.3.4", "@backstage/plugin-techdocs-backend": "^1.11.1", + "@internal/plugin-policy-common": "^0.1.0", "@roadiehq/scaffolder-backend-module-http-request": "^4.3.5", "@roadiehq/scaffolder-backend-module-utils": "^3.3.0", "app": "link:../app", diff --git a/packages/backend/src/extensions/policyProcessorExtension.ts b/packages/backend/src/extensions/policyProcessorExtension.ts index 0c6807f7..ceb7e406 100644 --- a/packages/backend/src/extensions/policyProcessorExtension.ts +++ b/packages/backend/src/extensions/policyProcessorExtension.ts @@ -2,6 +2,7 @@ import { Entity } from '@backstage/catalog-model'; import { createBackendModule } from '@backstage/backend-plugin-api'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { POLICY_KIND, policyValidator } from '@internal/plugin-policy-common'; class PolicyEntityProcessor implements CatalogProcessor { getProcessorName(): string { @@ -9,7 +10,7 @@ class PolicyEntityProcessor implements CatalogProcessor { } async validateEntityKind(entity: Entity): Promise { - return entity.kind === 'Policy'; + return entity.kind === POLICY_KIND && policyValidator.check(entity); } } diff --git a/plugins/policy-common/.eslintrc.js b/plugins/policy-common/.eslintrc.js new file mode 100644 index 00000000..e2a53a6a --- /dev/null +++ b/plugins/policy-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/policy-common/README.md b/plugins/policy-common/README.md new file mode 100644 index 00000000..20644a09 --- /dev/null +++ b/plugins/policy-common/README.md @@ -0,0 +1,5 @@ +# backstage-plugin-policy-common + +Welcome to the common package for the policy plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/policy-common/package.json b/plugins/policy-common/package.json new file mode 100644 index 00000000..6b7ca258 --- /dev/null +++ b/plugins/policy-common/package.json @@ -0,0 +1,37 @@ +{ + "name": "@internal/plugin-policy-common", + "description": "Common functionalities for the policy plugin", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "sideEffects": false, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/catalog-model": "^1.7.3", + "zod": "^3.24.2" + }, + "devDependencies": { + "@backstage/cli": "^0.28.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/policy-common/src/index.ts b/plugins/policy-common/src/index.ts new file mode 100644 index 00000000..cd4fa2f7 --- /dev/null +++ b/plugins/policy-common/src/index.ts @@ -0,0 +1 @@ +export * from './models/policy'; diff --git a/plugins/policy-common/src/models/policy.ts b/plugins/policy-common/src/models/policy.ts new file mode 100644 index 00000000..e6488d9e --- /dev/null +++ b/plugins/policy-common/src/models/policy.ts @@ -0,0 +1,43 @@ +import { z } from 'zod'; +import { KindValidator } from '@backstage/catalog-model'; + +export const POLICY_KIND = 'Policy'; +export const POLICY_VERSION = ['bc-gov/policyv1'] as const; +export const POLICY_TYPES = [ + 'security', + 'best-practice', + 'code-quality', +] as const; +export const POLICY_LEVELS = [ + 'optional', + 'recommended', + 'required', + 'strictly-enforced', +] as const; + +const policySchema = z.object({ + apiVersion: z.enum(POLICY_VERSION), + kind: z.literal(POLICY_KIND), + metadata: z.object({ + name: z.string(), + title: z.string(), + description: z.string(), + }), + spec: z.object({ + type: z.enum(POLICY_TYPES), + level: z.enum(POLICY_LEVELS), + }), +}); + +export type Policy = z.infer; + +export const policyValidator: KindValidator = { + async check(entity) { + try { + await policySchema.parseAsync(entity); + return true; + } catch (e) { + throw new Error(`Policy validation failed: ${e}`); + } + }, +}; diff --git a/plugins/policy-common/src/setupTests.ts b/plugins/policy-common/src/setupTests.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/plugins/policy-common/src/setupTests.ts @@ -0,0 +1 @@ +export {}; diff --git a/yarn.lock b/yarn.lock index b8347989..80c4aaaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9959,6 +9959,18 @@ dependencies: "@types/node" "*" +"@types/lodash-es@^4.17.12": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.15" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.15.tgz#12d4af0ed17cc7600ce1f9980cec48fc17ad1e89" + integrity sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw== + "@types/long@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" @@ -11009,6 +11021,7 @@ apg-lite@^1.0.3: "@bcgov/bc-sans" "^2.0.0" "@bcgov/design-tokens" "^3.0.0" "@internal/plugin-analytics-module-snowplow" "^0.1.0" + "@internal/plugin-policy-common" "^0.1.0" "@material-ui/core" "^4.12.4" "@material-ui/icons" "^4.11.3" "@material-ui/lab" "^4.0.0-alpha.61" @@ -11019,6 +11032,7 @@ apg-lite@^1.0.3: "@snowplow/browser-tracker" "^3.15.0" backstage-plugin-techdocs-addon-mermaid "^0.13.0" history "^5.0.0" + lodash-es "^4.17.21" react "^18.0.2" react-dom "^18.0.2" react-router "^6.3.0" @@ -27006,6 +27020,11 @@ zod@^3.22.4: resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== +zod@^3.24.2: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== + zstd-codec@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/zstd-codec/-/zstd-codec-0.1.5.tgz#c180193e4603ef74ddf704bcc835397d30a60e42" From eed3e6460196d20830b3f6761bd688a37d3220f7 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Mon, 24 Feb 2025 22:31:52 -0500 Subject: [PATCH 06/79] Added policy icon --- packages/app/src/apis.ts | 22 ++- packages/app/src/components/utils/icons.tsx | 157 +++++++++++++++----- 2 files changed, 139 insertions(+), 40 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 5e3f84d3..ae0c02bd 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -9,9 +9,29 @@ import { createApiFactory, analyticsApiRef, } from '@backstage/core-plugin-api'; -import { SnowplowAnalytics } from '@internal/plugin-analytics-module-snowplow' +import { SnowplowAnalytics } from '@internal/plugin-analytics-module-snowplow'; +import { + catalogApiRef, + entityPresentationApiRef, +} from '@backstage/plugin-catalog-react'; +import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; +import { PolicyIcon } from './components/utils/icons'; export const apis: AnyApiFactory[] = [ + createApiFactory({ + api: entityPresentationApiRef, + deps: { + catalgoApi: catalogApiRef, + }, + factory({ catalgoApi }) { + return DefaultEntityPresentationApi.create({ + catalogApi: catalgoApi, + kindIcons: { + Policy: PolicyIcon, + }, + }); + }, + }), createApiFactory({ api: scmIntegrationsApiRef, deps: { configApi: configApiRef }, diff --git a/packages/app/src/components/utils/icons.tsx b/packages/app/src/components/utils/icons.tsx index 68dc9b17..5f546280 100644 --- a/packages/app/src/components/utils/icons.tsx +++ b/packages/app/src/components/utils/icons.tsx @@ -1,48 +1,127 @@ - - -import {createSvgIcon} from "@material-ui/core"; -import React from "react"; +import { createSvgIcon } from '@material-ui/core'; +import React from 'react'; // @see https://icon-sets.iconify.design/logos/rocket-chat-icon/ -export const RocketChatIcon = createSvgIcon( - - - , 'RocketChat' +export const RocketChatIcon = createSvgIcon( + + + + , + 'RocketChat', ); // @see https://icon-sets.iconify.design/devicon/github/ -export const GitHubSvgIcon = createSvgIcon( - - - - - -, "GitHub"); +export const GitHubSvgIcon = createSvgIcon( + + + + + + + , + 'GitHub', +); // @see https://icon-sets.iconify.design/logos/stackoverflow-icon/ -export const StackOverFlowIcon = createSvgIcon( - - - -, "StackOverflow"); +export const StackOverFlowIcon = createSvgIcon( + + + + + , + 'StackOverflow', +); // @see https://icon-sets.iconify.design/logos/openshift/ -export const OpenShiftSvgIcon = createSvgIcon( - - - - - - - - - , "OpenShift"); \ No newline at end of file +export const OpenShiftSvgIcon = createSvgIcon( + + + + + + + + + + , + 'OpenShift', +); + +export const PolicyIcon = createSvgIcon( + + + + + + + + + + + + , + 'Policy', +); From dad9b959590d109a4f28a47cbe1c330766dcfc4d Mon Sep 17 00:00:00 2001 From: Min Kim Date: Mon, 24 Feb 2025 22:04:06 -0500 Subject: [PATCH 07/79] Add policy rows in card for components --- .../EntityPoliciesCard/EntityPoliciesCard.tsx | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx index 988855f4..84bd4c14 100644 --- a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx +++ b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx @@ -6,9 +6,11 @@ import Divider from '@material-ui/core/Divider'; import CardContent from '@material-ui/core/CardContent'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; import { InfoCardVariants } from '@backstage/core-components'; import { makeStyles } from '@material-ui/core/styles'; import { useEntity } from '@backstage/plugin-catalog-react'; +import { Link } from '@material-ui/core'; interface PoliciesCardProps { variant?: InfoCardVariants; @@ -46,8 +48,51 @@ const useStyles = makeStyles({ cardContent: { flex: 1, }, + policyRowContainer: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginTop: '10px', + marginBottom: '12px', + }, + policyRowContent: { + display: 'flex', + alignItems: 'center', + }, + policyRowDescription: { + marginLeft: '15px', + }, + policyRowCheckbox: { + fill: '#00be00', + fontSize: 40, + }, }); +const exampleComponentPolicies = [ + 'Component performs dependency chain analysis', + 'Container vulnerability scanning report is 34 days old', + 'Repository uses an automated dependency update tool', + 'No crticial alerts older than 30 days', + 'Repository does not contain unencrypted secrets', +]; + +const PolicyRow = ({ description }: { description: string }) => { + const classes = useStyles(); + return ( + + + + + {description} + + + + Learn more + + + ); +}; + export function EntityPoliciesCard(props: PoliciesCardProps) { const { variant } = props; const classes = useStyles(); @@ -74,7 +119,14 @@ export function EntityPoliciesCard(props: PoliciesCardProps) { } /> - + + {exampleComponentPolicies.map(policy => ( + <> + + + + ))} + ); } From 2e20135ed70eb68e0cfd8aa3c8c88926e232fc92 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Mon, 24 Feb 2025 22:29:54 -0500 Subject: [PATCH 08/79] Make rating configurable and add policy card to systems page --- .../app/src/components/catalog/EntityPage.tsx | 25 +++++----- .../EntityPoliciesCard/EntityPoliciesCard.tsx | 47 ++++++++++++++----- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 227b95d8..e43c5c39 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -4,7 +4,7 @@ import { EntityApiDefinitionCard, EntityConsumedApisCard, EntityConsumingComponentsCard, - EntityHasApisCard, + // EntityHasApisCard, EntityProvidedApisCard, EntityProvidingComponentsCard, } from '@backstage/plugin-api-docs'; @@ -12,8 +12,8 @@ import { EntityAboutCard, EntityDependsOnComponentsCard, EntityDependsOnResourcesCard, - EntityHasComponentsCard, - EntityHasResourcesCard, + // EntityHasComponentsCard, + // EntityHasResourcesCard, EntityHasSystemsCard, EntityLayout, EntityLinksCard, @@ -146,7 +146,7 @@ const overviewContent = ( */} - + ); @@ -358,17 +358,20 @@ const systemPage = ( - + {/* - - + */} + {/* - - + */} + {/* - - + */} + {/* + */} + + diff --git a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx index 84bd4c14..0e2c1c05 100644 --- a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx +++ b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx @@ -14,8 +14,16 @@ import { Link } from '@material-ui/core'; interface PoliciesCardProps { variant?: InfoCardVariants; + rating?: string | undefined; } +const ratingChip = { + borderRadius: '10px', + width: '90px', + marginLeft: '10px', + color: 'gray', +}; + const useStyles = makeStyles({ header: { display: 'flex', @@ -26,13 +34,15 @@ const useStyles = makeStyles({ display: 'flex', alignItems: 'center', }, - goldRating: { + ratingGold: { + ...ratingChip, backgroundColor: 'gold', - border: '3px solid orange', - borderRadius: '10px', - padding: '5px', - marginLeft: '10px', - color: 'gray', + border: '3px solid yellow', + }, + ratingBronze: { + ...ratingChip, + backgroundColor: '#faaf08', + border: '3px solid gold', }, gridItemCard: { display: 'flex', @@ -93,8 +103,26 @@ const PolicyRow = ({ description }: { description: string }) => { ); }; +const PolicyRating = ({ rating }: { rating: string | undefined }) => { + const classes = useStyles(); + if (rating === 'GOLD') { + return ( + + Security Rating + + + ); + } + return ( + + Security Rating + + + ); +}; + export function EntityPoliciesCard(props: PoliciesCardProps) { - const { variant } = props; + const { variant, rating } = props; const classes = useStyles(); const { entity } = useEntity(); @@ -111,10 +139,7 @@ export function EntityPoliciesCard(props: PoliciesCardProps) { title={ Security Policies - - Security Rating - - + } /> From 65ea44ec46c8f78f91c30cf471a73a26122c312c Mon Sep 17 00:00:00 2001 From: Min Kim Date: Mon, 24 Feb 2025 22:56:23 -0500 Subject: [PATCH 09/79] Create second variation of policy card for systems --- .../EntityPoliciesCard/EntityPoliciesCard.tsx | 112 +++++++++++++++--- 1 file changed, 94 insertions(+), 18 deletions(-) diff --git a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx index 0e2c1c05..701712ac 100644 --- a/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx +++ b/packages/app/src/components/catalog/EntityPoliciesCard/EntityPoliciesCard.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import Avatar from '@material-ui/core/Avatar'; import Card from '@material-ui/core/Card'; import Chip from '@material-ui/core/Chip'; import CardHeader from '@material-ui/core/CardHeader'; @@ -12,11 +13,6 @@ import { makeStyles } from '@material-ui/core/styles'; import { useEntity } from '@backstage/plugin-catalog-react'; import { Link } from '@material-ui/core'; -interface PoliciesCardProps { - variant?: InfoCardVariants; - rating?: string | undefined; -} - const ratingChip = { borderRadius: '10px', width: '90px', @@ -24,6 +20,12 @@ const ratingChip = { color: 'gray', }; +const avatar = { + color: 'black', + padding: '20px', + marginRight: '15px', +}; + const useStyles = makeStyles({ header: { display: 'flex', @@ -67,14 +69,21 @@ const useStyles = makeStyles({ }, policyRowContent: { display: 'flex', + flex: 4, alignItems: 'center', }, - policyRowDescription: { - marginLeft: '15px', - }, policyRowCheckbox: { fill: '#00be00', fontSize: 40, + marginRight: '15px', + }, + red: { + ...avatar, + backgroundColor: 'pink', + }, + green: { + ...avatar, + backgroundColor: 'lime', }, }); @@ -86,15 +95,75 @@ const exampleComponentPolicies = [ 'Repository does not contain unencrypted secrets', ]; -const PolicyRow = ({ description }: { description: string }) => { +const exampleSystemPolicies = [ + { + red: 3, + green: 7, + description: + 'All components in the system perform dependency chain analysis', + }, + { + red: 0, + green: 5, + description: + 'All services in the system have container vulnerability scanning reports that are <90 days old', + }, + { + red: 10, + green: 0, + description: + 'All components in the system are configured to automatically update dependencies', + }, + { + red: 47, + green: 0, + description: + 'Components do not have dependency CRITICAL alerts older than 30 days', + }, + { + red: 0, + green: 10, + description: + 'Components do not store unencrypted credentials in the repository', + }, +]; + +interface PoliciesCardProps { + variant?: InfoCardVariants; + rating?: string | undefined; +} + +const PolicyRow1 = ({ policy }: { policy: string }) => { const classes = useStyles(); return ( - - {description} - + {policy} + + + Learn more + + + ); +}; + +const PolicyRow2 = ({ + policy: { red, green, description }, +}: { + policy: { + red: number; + green: number; + description: string; + }; +}) => { + const classes = useStyles(); + return ( + + + {red} + {green} + {description} Learn more @@ -145,12 +214,19 @@ export function EntityPoliciesCard(props: PoliciesCardProps) { /> - {exampleComponentPolicies.map(policy => ( - <> - - - - ))} + {entity.kind === 'Component' + ? exampleComponentPolicies.map(policy => ( + <> + + + + )) + : exampleSystemPolicies.map(policy => ( + <> + + + + ))} ); From 873e02edc48be8e2ae96a22b197b90090fa37557 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Tue, 25 Feb 2025 09:14:52 -0500 Subject: [PATCH 10/79] Enable catalog in side nav --- packages/app/src/components/Root/Root.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index ab50532e..14bacd24 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -1,7 +1,7 @@ import React, { PropsWithChildren } from 'react'; import { makeStyles } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; -// import CatalogIcon from '@material-ui/icons/LocalLibrary'; +import CatalogIcon from '@material-ui/icons/LocalLibrary'; // import ExtensionIcon from '@material-ui/icons/Extension'; // import MapIcon from '@material-ui/icons/MyLocation'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; @@ -117,7 +117,8 @@ export const Root = ({ children }: PropsWithChildren<{}>) => { }> {/* Global nav, not org-specific */} - {/* + + {/* Date: Tue, 25 Feb 2025 19:19:41 -0500 Subject: [PATCH 11/79] Explicitly add policies card for systems and components overview pages --- .../app/src/components/catalog/EntityPage.tsx | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index e43c5c39..8cd5c245 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -4,7 +4,6 @@ import { EntityApiDefinitionCard, EntityConsumedApisCard, EntityConsumingComponentsCard, - // EntityHasApisCard, EntityProvidedApisCard, EntityProvidingComponentsCard, } from '@backstage/plugin-api-docs'; @@ -12,8 +11,7 @@ import { EntityAboutCard, EntityDependsOnComponentsCard, EntityDependsOnResourcesCard, - // EntityHasComponentsCard, - // EntityHasResourcesCard, + EntityHasSubcomponentsCard, EntityHasSystemsCard, EntityLayout, EntityLinksCard, @@ -58,9 +56,11 @@ import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; import { Mermaid } from 'backstage-plugin-techdocs-addon-mermaid'; -import { EntitySecurityInsightsContent } from '@roadiehq/backstage-plugin-security-insights'; - import { EntityPoliciesCard } from './EntityPoliciesCard'; +import { + componentSecurityAlertsContent, + systemSecurityAlertsContent, +} from './SecurityAlerts'; const techdocsContent = ( @@ -137,14 +137,24 @@ const overviewContent = ( + + + + + + + +); - {/* - - - - - */} - +const overviewContentWithPolicies = ( + + {entityWarningContent} + + + + + + @@ -154,7 +164,7 @@ const overviewContent = ( const serviceEntityPage = ( - {overviewContent} + {overviewContentWithPolicies} @@ -187,8 +197,8 @@ const serviceEntityPage = ( {techdocsContent} - - + + {componentSecurityAlertsContent} ); @@ -196,7 +206,7 @@ const serviceEntityPage = ( const websiteEntityPage = ( - {overviewContent} + {overviewContentWithPolicies} @@ -218,8 +228,8 @@ const websiteEntityPage = ( {techdocsContent} - - + + {componentSecurityAlertsContent} ); @@ -256,6 +266,10 @@ const defaultEntityPage = ( {techdocsContent} + + + {componentSecurityAlertsContent} + ); @@ -394,6 +408,9 @@ const systemPage = ( unidirectional={false} /> + + {systemSecurityAlertsContent} + ); From 085bc9706482ec35c996acee70daf5b2b0367bc9 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Tue, 25 Feb 2025 19:20:22 -0500 Subject: [PATCH 12/79] Add example alerts entity and processor --- app-config.yaml | 17 +++++- examples/alerts.yaml | 60 +++++++++++++++++++ .../src/extensions/alertProcessorExtension.ts | 31 ++++++++++ packages/backend/src/index.ts | 1 + 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 examples/alerts.yaml create mode 100644 packages/backend/src/extensions/alertProcessorExtension.ts diff --git a/app-config.yaml b/app-config.yaml index 49992f6d..abf053cc 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -116,7 +116,22 @@ catalog: timeout: { minutes: 15 } rules: - allow: - [Component, API, Location, Template, Resource, System, Group, Domain] + [ + Component, + API, + Location, + Template, + Resource, + System, + Group, + Domain, + Policy, + Alert, + ] locations: - type: file target: ../../catalog-info.yaml + - type: file + target: ../../examples/policies-generated.yaml + - type: file + target: ../../examples/alerts.yaml diff --git a/examples/alerts.yaml b/examples/alerts.yaml new file mode 100644 index 00000000..e419a429 --- /dev/null +++ b/examples/alerts.yaml @@ -0,0 +1,60 @@ +apiVersion: bc-gov/alertsv1 +kind: Alert +metadata: + name: dependency-analysis + description: Low risk vulnerability detected in node-cron +spec: + category: Dependencies + level: Recommended + severity: Warning + entity: developer-portal + entityType: Component +--- +apiVersion: bc-gov/alertsv1 +kind: Alert +metadata: + name: system-dependency + description: openssl vulnerability detected +spec: + category: Runtime + level: Required + severity: Warning + entity: devhub-db + entityType: Resource +--- +apiVersion: bc-gov/alertsv1 +kind: Alert +metadata: + name: system-dependency-2 + title: system-dependency + description: Low risk vulnerability detected in React +spec: + category: Dependencies + level: Recommended + severity: Info + entity: emerald + entityType: Resource +--- +apiVersion: bc-gov/alertsv1 +kind: Alert +metadata: + name: exposed-secrets + description: Found possible secrets in repository +spec: + category: Secrets + level: Required + severity: Critical + entity: gold-dr + entityType: Resource +--- +apiVersion: bc-gov/alertsv1 +kind: Alert +metadata: + name: automatic-dependency-updates + description: Automatic dependency updates were not detected +spec: + category: Dependencies + level: Optional + severity: Info + entity: f5ff48 + entityType: Resource diff --git a/packages/backend/src/extensions/alertProcessorExtension.ts b/packages/backend/src/extensions/alertProcessorExtension.ts new file mode 100644 index 00000000..c2e41f5e --- /dev/null +++ b/packages/backend/src/extensions/alertProcessorExtension.ts @@ -0,0 +1,31 @@ +import { Entity } from '@backstage/catalog-model'; +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; + +class AlertEntityProcessor implements CatalogProcessor { + getProcessorName(): string { + return 'AlertEntityProcessor'; + } + + async validateEntityKind(entity: Entity): Promise { + return entity.kind === 'Alert'; + } +} + +export const catalogModuleAlertProcessor = createBackendModule({ + pluginId: 'catalog', + moduleId: 'alert-processor', + register(env) { + env.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + }, + async init({ catalog }) { + catalog.addProcessor(new AlertEntityProcessor()); + }, + }); + }, +}); + +export { catalogModuleAlertProcessor as default }; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 5c3002f6..bccf43d3 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -28,6 +28,7 @@ backend.add(import('@backstage/plugin-techdocs-backend')); backend.add(import('@backstage/plugin-permission-backend')); backend.add(import('./extensions/permissionsPolicyExtension')); backend.add(import('./extensions/policyProcessorExtension')); +backend.add(import('./extensions/alertProcessorExtension')); backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); backend.start(); From 68e451e0f91d84150ec7fd55762ddb9d0f04a63c Mon Sep 17 00:00:00 2001 From: Min Kim Date: Tue, 25 Feb 2025 19:21:06 -0500 Subject: [PATCH 13/79] Specify default filters to exclude alerts entities --- packages/app/src/App.tsx | 8 +++- packages/app/src/filters.tsx | 77 ++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/filters.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 1e1a6b8e..5a26020f 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -47,6 +47,7 @@ import { TechdocExpandableToc } from '@app/plugin-expandable-toc'; import { Mermaid } from 'backstage-plugin-techdocs-addon-mermaid'; import { Custom404Page } from './components/404/Custom404Page'; import { columns } from './components/utils/columns'; +import { DefaultFilters } from './filters'; const app = createApp({ apis, @@ -115,7 +116,12 @@ const routes = ( } /> - } /> + } /> + } + /> } diff --git a/packages/app/src/filters.tsx b/packages/app/src/filters.tsx new file mode 100644 index 00000000..b8ff0f4f --- /dev/null +++ b/packages/app/src/filters.tsx @@ -0,0 +1,77 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; + +import { + UserListFilterKind, + EntityKindPicker, + EntityLifecyclePicker, + EntityNamespacePicker, + EntityOwnerPickerProps, + EntityOwnerPicker, + EntityProcessingStatusPicker, + EntityTagPicker, + EntityTypePicker, + UserListPicker, +} from '@backstage/plugin-catalog-react'; + +/** + * Props for default filters. + * + * @public + */ +export type DefaultFiltersProps = { + initialKind?: string; + initiallySelectedFilter?: UserListFilterKind; + ownerPickerMode?: EntityOwnerPickerProps['mode']; + initiallySelectedNamespaces?: string[]; +}; + +/** @public */ +export const DefaultFilters = (props: DefaultFiltersProps) => { + const { + initialKind, + initiallySelectedFilter, + ownerPickerMode, + initiallySelectedNamespaces, + } = props; + return ( + <> + + + + + + + + + + ); +}; From 52711ed130eb7e61fd399adf27426973ba711049 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Tue, 25 Feb 2025 19:25:56 -0500 Subject: [PATCH 14/79] Match up policy names in alerts --- examples/alerts.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/alerts.yaml b/examples/alerts.yaml index e419a429..29ae6783 100644 --- a/examples/alerts.yaml +++ b/examples/alerts.yaml @@ -14,6 +14,7 @@ apiVersion: bc-gov/alertsv1 kind: Alert metadata: name: system-dependency + title: critical-dependencies description: openssl vulnerability detected spec: category: Runtime @@ -25,7 +26,7 @@ spec: apiVersion: bc-gov/alertsv1 kind: Alert metadata: - name: system-dependency-2 + name: critical-dependencies title: system-dependency description: Low risk vulnerability detected in React spec: @@ -38,7 +39,7 @@ spec: apiVersion: bc-gov/alertsv1 kind: Alert metadata: - name: exposed-secrets + name: unencrypted-credentials description: Found possible secrets in repository spec: category: Secrets @@ -50,7 +51,7 @@ spec: apiVersion: bc-gov/alertsv1 kind: Alert metadata: - name: automatic-dependency-updates + name: automatic-updates description: Automatic dependency updates were not detected spec: category: Dependencies From 4c41c3c15f5cd598d66db00192a4df924d6b1339 Mon Sep 17 00:00:00 2001 From: Min Kim Date: Tue, 25 Feb 2025 19:35:13 -0500 Subject: [PATCH 15/79] Create table in alerts tab for components --- examples/alerts.yaml | 6 +- .../catalog/SecurityAlerts/SecurityAlerts.tsx | 38 +++++++ .../catalog/SecurityAlerts/columns.tsx | 100 ++++++++++++++++++ .../catalog/SecurityAlerts/filters.tsx | 78 ++++++++++++++ .../catalog/SecurityAlerts/index.ts | 4 + 5 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 packages/app/src/components/catalog/SecurityAlerts/SecurityAlerts.tsx create mode 100644 packages/app/src/components/catalog/SecurityAlerts/columns.tsx create mode 100644 packages/app/src/components/catalog/SecurityAlerts/filters.tsx create mode 100644 packages/app/src/components/catalog/SecurityAlerts/index.ts diff --git a/examples/alerts.yaml b/examples/alerts.yaml index 29ae6783..9fa19a4d 100644 --- a/examples/alerts.yaml +++ b/examples/alerts.yaml @@ -15,7 +15,7 @@ kind: Alert metadata: name: system-dependency title: critical-dependencies - description: openssl vulnerability detected + description: OpenSSL vulnerability detected spec: category: Runtime level: Required @@ -26,8 +26,8 @@ spec: apiVersion: bc-gov/alertsv1 kind: Alert metadata: - name: critical-dependencies - title: system-dependency + name: system-dependency + title: critical-dependencies description: Low risk vulnerability detected in React spec: category: Dependencies diff --git a/packages/app/src/components/catalog/SecurityAlerts/SecurityAlerts.tsx b/packages/app/src/components/catalog/SecurityAlerts/SecurityAlerts.tsx new file mode 100644 index 00000000..b1b97df2 --- /dev/null +++ b/packages/app/src/components/catalog/SecurityAlerts/SecurityAlerts.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Grid } from '@material-ui/core'; +import { + EntityListProvider, + CatalogFilterLayout, + EntityKindPicker, +} from '@backstage/plugin-catalog-react'; +import { CatalogTable } from '@backstage/plugin-catalog'; +import { + EntityAlertCategoryPicker, + EntityAlertLevelPicker, + EntityAlertSeverityPicker, +} from './filters'; +import { componentAlertsColumns, systemAlertsColumns } from './columns'; + +export const componentSecurityAlertsContent = ( + + + + + + + + + +); + +export const systemSecurityAlertsContent = ( + + + header with rating + + +); diff --git a/packages/app/src/components/catalog/SecurityAlerts/columns.tsx b/packages/app/src/components/catalog/SecurityAlerts/columns.tsx new file mode 100644 index 00000000..b1bc9c10 --- /dev/null +++ b/packages/app/src/components/catalog/SecurityAlerts/columns.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { + CatalogTableColumnsFunc, + CatalogTableRow, +} from '@backstage/plugin-catalog'; +import { Theme, Typography, makeStyles } from '@material-ui/core'; +import { TableColumn } from '@backstage/core-components'; +import { startCase, camelCase } from 'lodash-es'; +import { EntityRefLink } from '@backstage/plugin-catalog-react'; + +function prettyText(text: string) { + return startCase(camelCase(text)); +} + +const useStyles = makeStyles((theme: Theme) => ({ + optional: { + color: theme.palette.success.main, + }, + recommended: { + color: theme.palette.primary.main, + }, + required: { + color: theme.palette.warning.main, + }, + 'strictly-enforced': { + color: theme.palette.error.main, + }, +})); + +// columns [ entity, type ] + +const alertColumn: TableColumn = { + title: 'Alert', + field: 'metadata.description', + width: '35%', + render: ({ entity }) => entity.metadata?.description, +}; + +const severityColumn: TableColumn = { + title: 'Severity', + field: 'spec.severity', + width: '15%', + render: row => { + return row.entity.spec?.severity?.toString() || ''; + }, +}; + +const policyColumn: TableColumn = { + title: 'Policy Category', + field: 'spec.category', + width: '15%', + render: row => ( + + ), +}; + +const policyCategoryColumn: TableColumn = { + title: 'Policy Category', + field: 'spec.category', + width: '15%', + render: row => { + return row.entity.spec?.category?.toString() || ''; + }, +}; + +const levelColumn: TableColumn = { + title: 'Level', + field: 'spec.level', + width: '15%', + render: row => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const classes = useStyles(); + const level = row.entity.spec?.level + ?.toString() + .toLowerCase() as keyof typeof classes; + return ( + + {prettyText(level) || ''} + + ); + }, +}; + +export const componentAlertsColumns: CatalogTableColumnsFunc = () => { + return [ + alertColumn, + severityColumn, + policyColumn, + policyCategoryColumn, + levelColumn, + ]; +}; + +export const systemAlertsColumns: CatalogTableColumnsFunc = () => { + return []; +}; diff --git a/packages/app/src/components/catalog/SecurityAlerts/filters.tsx b/packages/app/src/components/catalog/SecurityAlerts/filters.tsx new file mode 100644 index 00000000..4ee82f3f --- /dev/null +++ b/packages/app/src/components/catalog/SecurityAlerts/filters.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import Box from '@material-ui/core/Box'; +import { Select } from '@backstage/core-components'; + +export const EntityAlertCategoryPicker = () => { + return ( + + {}} + /> + + ); +}; + +export const EntityAlertSeverityPicker = () => { + return ( + +