├── .gitignore ├── Makefile ├── src ├── preferences │ ├── carbonetes-preferences.scss │ ├── carbonetes-preference-store.ts │ └── carbonetes-preferences.tsx ├── utils │ ├── constants.ts │ └── helper.ts ├── service │ ├── axios.ts │ └── requests.ts └── details │ ├── carbonetes-details.scss │ ├── vulnerabilities.tsx │ ├── policy-evaluation.tsx │ ├── complete-analysis.tsx │ └── carbonetes-details.tsx ├── docs └── images │ ├── example.png │ ├── image.png │ ├── complete-analysis.png │ ├── manage-extension.png │ ├── policy-evaluation.png │ └── vulnerabilities.png ├── .whitesource ├── main.ts ├── tsconfig.json ├── renderer.tsx ├── .eslintrc.js ├── package.json ├── webpack.config.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install-deps: 2 | npm install 3 | 4 | build: install-deps 5 | npm run build 6 | -------------------------------------------------------------------------------- /src/preferences/carbonetes-preferences.scss: -------------------------------------------------------------------------------- 1 | .smallText { 2 | font-size: smaller !important; 3 | } -------------------------------------------------------------------------------- /docs/images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carbonetes/carbonetes-lens-extension/HEAD/docs/images/example.png -------------------------------------------------------------------------------- /docs/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carbonetes/carbonetes-lens-extension/HEAD/docs/images/image.png -------------------------------------------------------------------------------- /docs/images/complete-analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carbonetes/carbonetes-lens-extension/HEAD/docs/images/complete-analysis.png -------------------------------------------------------------------------------- /docs/images/manage-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carbonetes/carbonetes-lens-extension/HEAD/docs/images/manage-extension.png -------------------------------------------------------------------------------- /docs/images/policy-evaluation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carbonetes/carbonetes-lens-extension/HEAD/docs/images/policy-evaluation.png -------------------------------------------------------------------------------- /docs/images/vulnerabilities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carbonetes/carbonetes-lens-extension/HEAD/docs/images/vulnerabilities.png -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const REGISTRY = 'REGISTRY'; 2 | export const IMAGE_NAME = 'IMAGE_NAME'; 3 | export const NAMESPACES = ['kube-node-lease', 'kube-public', 'kube-system']; -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff" 8 | }, 9 | "issueSettings": { 10 | "minSeverityLevel": "LOW" 11 | } 12 | } -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { Main } from "@k8slens/extensions"; 2 | import { CarbonetesStore } from "./src/preferences/carbonetes-preference-store"; 3 | 4 | const { LensExtension } = Main; 5 | 6 | export default class ExampleExtensionMain extends LensExtension { 7 | async onActivate() { 8 | console.log('Carbonetes Extension activated'); 9 | await CarbonetesStore.createInstance().loadExtension(this); 10 | } 11 | 12 | onDeactivate() { 13 | console.log('Carbonetes Extension de-activated'); 14 | CarbonetesStore.resetInstance(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/service/axios.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | axios.defaults.timeout = 2700000; 3 | 4 | const axiosInstance = axios.create({ 5 | timeout: 2700000, 6 | headers : { 7 | 'Access-Control-Allow-Origin' : '*', 8 | 'Access-Control-Allow-Headers' : 'Origin, X-Requested-With, Content-Type', 9 | 'Access-Control-Allow-Methods' : 'DELETE, GET, OPTIONS, PATCH POST PUT', 10 | } 11 | }); 12 | 13 | axiosInstance.interceptors.request.use( 14 | async (config) => { 15 | return config; 16 | }, 17 | (error) => { 18 | return Promise.reject (error); 19 | } 20 | ); 21 | 22 | export default axiosInstance; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "CommonJS", 5 | "target": "ES2017", 6 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 7 | "moduleResolution": "Node", 8 | "sourceMap": false, 9 | "declaration": false, 10 | "strict": false, 11 | "noImplicitAny": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "allowSyntheticDefaultImports": true, 15 | "experimentalDecorators": true, 16 | "jsx": "react" 17 | }, 18 | "include": [ 19 | "./*.ts", 20 | "./*.tsx" 21 | ], 22 | "exclude": [ 23 | "node_modules", 24 | "*.js" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /renderer.tsx: -------------------------------------------------------------------------------- 1 | import { Renderer } from "@k8slens/extensions"; 2 | import { CarbonetesDetails, CarbonetesDetailsProps } from "./src/details/carbonetes-details" 3 | import { CarbonetesStore } from "./src/preferences/carbonetes-preference-store"; 4 | import { CarbonetesPreferenceHint, CarbonetesPreferenceInput } from "./src/preferences/carbonetes-preferences"; 5 | import React from "react"; 6 | 7 | const { LensExtension } = Renderer; 8 | export default class ExampleExtension extends LensExtension { 9 | appPreferences = [ 10 | { 11 | title: "Carbonetes", 12 | components: { 13 | Hint: () => , 14 | Input: () => 15 | } 16 | } 17 | ]; 18 | 19 | kubeObjectDetailItems = [ 20 | { 21 | kind: "Deployment", 22 | apiVersions: ["apps/v1"], 23 | components: { 24 | Details: (props: CarbonetesDetailsProps) => 25 | } 26 | } 27 | ] 28 | 29 | async onActivate() { 30 | console.log("Carbonetes extension activated"); 31 | await CarbonetesStore.createInstance().loadExtension(this); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/details/carbonetes-details.scss: -------------------------------------------------------------------------------- 1 | .whiteText { 2 | color: white !important; 3 | } 4 | 5 | .theme-critical { 6 | background-color: #cc1814 !important; 7 | } 8 | 9 | .theme-high { 10 | background-color: #ffa500 !important; 11 | } 12 | 13 | .theme-medium { 14 | background-color: #f0c20c !important; 15 | } 16 | 17 | .theme-low { 18 | background-color: #096ab0 !important; 19 | } 20 | 21 | .theme-negligible { 22 | background-color: #00b09b !important; 23 | } 24 | 25 | .theme-unknown { 26 | background-color: #515456 !important; 27 | } 28 | 29 | .theme-success { 30 | background-color: #00b09b !important; 31 | } 32 | 33 | .theme-warning { 34 | background-color: #f0c20c !important; 35 | } 36 | 37 | .theme-info { 38 | background-color: #096ab0 !important; 39 | } 40 | 41 | .theme-error { 42 | background-color: #cc1814 !important; 43 | } 44 | 45 | .theme-neutral { 46 | background-color: #515456 !important; 47 | } 48 | 49 | .license { 50 | background-color: transparent !important; 51 | color: #096ab0 !important; 52 | padding-left: 0 !important; 53 | } 54 | 55 | .threat { 56 | background-color: transparent !important; 57 | color: #ffa500 !important; 58 | padding-left: 0 !important; 59 | } 60 | 61 | ul.a { 62 | list-style-type: disc !important; 63 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | overrides: [ 3 | { 4 | files: [ 5 | "./**/*.ts" 6 | ], 7 | parser: "@typescript-eslint/parser", 8 | extends: [ 9 | 'plugin:@typescript-eslint/recommended', 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2018, 13 | sourceType: 'module', 14 | }, 15 | rules: { 16 | "indent": ["error", 2] 17 | }, 18 | }, 19 | { 20 | files: [ 21 | "./**/*.tsx", 22 | ], 23 | parser: "@typescript-eslint/parser", 24 | extends: [ 25 | 'plugin:@typescript-eslint/recommended', 26 | ], 27 | parserOptions: { 28 | ecmaVersion: 2018, 29 | sourceType: 'module', 30 | jsx: true, 31 | }, 32 | rules: { 33 | "indent": ["error", 2] 34 | }, 35 | }, 36 | { 37 | files: [ 38 | "./*.js" 39 | ], 40 | extends: [ 41 | 'eslint:recommended', 42 | ], 43 | env: { 44 | node: true 45 | }, 46 | parserOptions: { 47 | ecmaVersion: 2018, 48 | sourceType: 'module', 49 | }, 50 | rules: { 51 | "indent": ["error", 2], 52 | "no-unused-vars": "off", 53 | } 54 | }, 55 | ] 56 | }; 57 | -------------------------------------------------------------------------------- /src/service/requests.ts: -------------------------------------------------------------------------------- 1 | import axiosInstance from './axios' 2 | axiosInstance.defaults.timeout=2700000; 3 | 4 | const CARBONETES_WRAPPER_API = 'https://api.carbonetes.com'; 5 | 6 | const request = { 7 | signIn: (data: any) => axiosInstance.post( 8 | CARBONETES_WRAPPER_API + '/auth/signin', 9 | data 10 | ), 11 | 12 | getAndReloadCompayRegistry: (config: any) => axiosInstance.get( 13 | CARBONETES_WRAPPER_API + '/api/v1/company_registry/by_uri', 14 | config 15 | ), 16 | 17 | getRegistries: (config: any) => axiosInstance.get( 18 | CARBONETES_WRAPPER_API + '/api/v1/company_registry', 19 | config 20 | ), 21 | reloadRegistry: (config: any) => axiosInstance.post( 22 | CARBONETES_WRAPPER_API + '/api/v1/company_registry/reload_by_registryUri', 23 | { 24 | ...config.data, 25 | }, 26 | { 27 | headers: config.headers 28 | } 29 | 30 | ), 31 | checkAnalysisResult: (config: any) => axiosInstance.get( 32 | // CARBONETES_WRAPPER_API + '/api/v1/analysis/full-tag', 33 | CARBONETES_WRAPPER_API + '/api/v1/analysis/current-image', 34 | config 35 | ), 36 | analyzeImage: (data: any) => axiosInstance.post( 37 | CARBONETES_WRAPPER_API + '/api/v1/analysis/', 38 | data 39 | ), 40 | getAnalysisResult: (data: any) => axiosInstance.post( 41 | CARBONETES_WRAPPER_API + '/api/v1/analysis/get-result', 42 | data 43 | ), 44 | } 45 | 46 | export default request; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@k8slens/carbonetes-lens-extension", 3 | "publisher": "Carbonetes", 4 | "version": "0.1.6", 5 | "description": "Lens Carbonetes extension", 6 | "author": "Carbonetes (https://carbonetes.com/about-us)", 7 | "maintainers": [ 8 | { 9 | "name": "erickdelacruz", 10 | "email": "erick.delacruz@hoolisoftware.com" 11 | }, 12 | { 13 | "name": "kirkalynsantos", 14 | "email": "kirk.santos@carbonetes.com" 15 | } 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/carbonetes/carbonetes-lens-extension.git", 20 | "directory": "carbonetes-lens-extension" 21 | }, 22 | "engines": { 23 | "lens": "^6.0.1" 24 | }, 25 | "main": "dist/main.js", 26 | "renderer": "dist/renderer.js", 27 | "scripts": { 28 | "start": "webpack --watch", 29 | "build": "npm run clean && webpack", 30 | "clean": "rm -rf ./dist" 31 | }, 32 | "dependencies": { 33 | "axios": "^0.21.4", 34 | "react-open-doodles": "^1.0.5" 35 | }, 36 | "devDependencies": { 37 | "@k8slens/extensions": "^6.0.1", 38 | "@types/node": "^12.0.0", 39 | "@types/react": "^16.9.35", 40 | "css-loader": "^5.0.1", 41 | "mobx": "^6.0.4", 42 | "mobx-react": "^7.0.5", 43 | "moment": "^2.29.1", 44 | "sass": "^1.29.0", 45 | "sass-loader": "^10.1.0", 46 | "style-loader": "^2.0.0", 47 | "ts-loader": "^8.2.0", 48 | "ts-node": "^2.0.0", 49 | "typescript": "^4.5.5", 50 | "webpack": "^4.44.2", 51 | "webpack-cli": "^3.3.11" 52 | }, 53 | "license": "MIT", 54 | "homepage": "https://github.com/carbonetes/carbonetes-lens-extension", 55 | "bugs": { 56 | "url": "https://github.com/carbonetes/carbonetes-lens-extension/issues" 57 | }, 58 | "keywords": [ 59 | "example", 60 | "extension", 61 | "k8slens", 62 | "lens", 63 | "mirantis", 64 | "tutorial" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = [ 4 | { 5 | entry: './main.ts', 6 | context: __dirname, 7 | target: "electron-main", 8 | mode: "production", 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.tsx?$/, 13 | use: 'ts-loader', 14 | exclude: /node_modules/, 15 | }, 16 | ], 17 | }, 18 | externals: [ 19 | { 20 | "@k8slens/extensions": "var global.LensExtensions", 21 | "mobx": "var global.Mobx", 22 | "react": "var global.React", 23 | "mobx-react": "var global.MobxReact" 24 | } 25 | ], 26 | resolve: { 27 | extensions: [ '.tsx', '.ts', '.js' ], 28 | }, 29 | output: { 30 | libraryTarget: "commonjs2", 31 | filename: 'main.js', 32 | path: path.resolve(__dirname, 'dist'), 33 | }, 34 | }, 35 | { 36 | entry: './renderer.tsx', 37 | context: __dirname, 38 | target: "electron-renderer", 39 | mode: "production", 40 | module: { 41 | rules: [ 42 | { 43 | test: /\.tsx?$/, 44 | use: 'ts-loader', 45 | exclude: /node_modules/, 46 | }, 47 | { 48 | test: /\.s?css$/, 49 | use: [ 50 | "style-loader", 51 | "css-loader", 52 | "sass-loader", 53 | ] 54 | } 55 | ], 56 | }, 57 | externals: [ 58 | { 59 | "@k8slens/extensions": "var global.LensExtensions", 60 | "react": "var global.React", 61 | "mobx": "var global.Mobx", 62 | "mobx-react": "var global.MobxReact" 63 | } 64 | ], 65 | resolve: { 66 | extensions: [ '.tsx', '.ts', '.js' ], 67 | }, 68 | output: { 69 | libraryTarget: "commonjs2", 70 | globalObject: "this", 71 | filename: 'renderer.js', 72 | path: path.resolve(__dirname, 'dist'), 73 | }, 74 | node: { 75 | __dirname: false, 76 | __filename: false 77 | } 78 | }, 79 | ]; 80 | -------------------------------------------------------------------------------- /src/utils/helper.ts: -------------------------------------------------------------------------------- 1 | export const getAnalysisStatus = (status: string) => { 2 | let analysisStatus: string; 3 | 4 | if (status === 'analyzed') { 5 | analysisStatus = 'Analyzed' 6 | } else if (status === 'waiting_for_analysis') { 7 | analysisStatus = 'Waiting for Analysis' 8 | } else if (status === 'analyzing') { 9 | analysisStatus = 'Analyzing' 10 | } else if (status === 'analysis_failed') { 11 | analysisStatus = 'Analysis Failed' 12 | } else if (status === 'not_analyzed') { 13 | analysisStatus = 'Not Analyzed' 14 | } else { 15 | analysisStatus = status; 16 | } 17 | 18 | return analysisStatus; 19 | } 20 | 21 | export const getSeverityStyle = (severity: string) => { 22 | let className: string; 23 | severity = severity.toLowerCase(); 24 | 25 | if (severity === 'critical') { 26 | className = 'theme-critical'; 27 | } else if (severity === 'high') { 28 | className = 'theme-high'; 29 | } else if (severity === 'medium') { 30 | className = 'theme-medium'; 31 | } else if (severity === 'low') { 32 | className = 'theme-low'; 33 | } else if (severity === 'negligible') { 34 | className = 'theme-negligible'; 35 | } else if (severity === 'unknown') { 36 | className = 'theme-unknown'; 37 | } 38 | 39 | return className; 40 | } 41 | 42 | export const getStatusStyle = (status: string) => { 43 | let className: string; 44 | status = status.toLowerCase() 45 | 46 | if (status === 'analyzed' || status === 'PASSED' || status.toUpperCase() === 'GO') { 47 | className = 'theme-success'; 48 | } else if (status === 'waiting_for_analysis' || status.toUpperCase() === 'WARN') { 49 | className = 'theme-warning'; 50 | } else if (status === 'analyzing') { 51 | className = 'theme-info'; 52 | } else if (status === 'analysis_failed' || status === 'FAILED' || status.toUpperCase() === 'STOP') { 53 | className = 'theme-error'; 54 | } else { 55 | className = 'theme-neutral'; 56 | } 57 | 58 | return className; 59 | } 60 | 61 | export const getValue = (isExists: boolean, value: any) => { 62 | 63 | return isExists ? value : '_'; 64 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Carbonetes Lens Extension 2 | 3 | This is a [Lens](https://k8slens.dev/) Extension for [Carbonetes](https://carbonetes.com/) provides the most comprehensive and complete Container Application Security Testing (CAST) solution on the market with best-in-class results. 4 | 5 | [![Screenshot](docs/images/example.png)](https://youtu.be/X-bhVwmp2l4) 6 | 7 | ## Prerequisites 8 | * Install [Lens](https://github.com/lensapp/lens/releases/latest) application. 9 | * You need to have a [Carbonetes](https://console.carbonetes.com) account. Sign Up [here](https://console.carbonetes.com/register). 10 | 11 | Note: 12 | Image Analysis can only be used on images that are in container registries added through Carbonetes application 13 | 14 | 15 | ## Install 16 | #### From the Binary Releases 17 | 1. Download your desired version from GitHub [release](https://github.com/carbonetes/carbonetes-lens-extension/releases) page. Alternatively, just copy the download URL of the release artifact. 18 | 2. Open Lens application and select **Lens** menu, and then click **Extensions** item, or press 19 | Shift + Command + E to open the **Manage Lens Extensions** page. 20 | 3. Specify the path of the extension package `.tar`, `.tgz` or the download URL and click **Install**. 21 | 4. If everything is fine, you'll see the `@carbonetes/carbonetes-lens-extension` extension listed under 22 | **Installed Extensions**. Click **Enable** to enable it. 23 | 24 | #### From Source (Linux, macOS) 25 | * You need to have [node](https://nodejs.org/en/) and [npm](https://nodejs.org/en/) installed on your system. It is recommended to use the node version used for Lens development itself which is documented [here](https://github.com/lensapp/lens#development). 26 | 27 | 1. Clone the source code to the ~/.k8slens/extensions/carbonetes-lens-extensions directory: 28 | ```sh 29 | mkdir -p ~/.k8slens/extensions 30 | git clone https://github.com/carbonetes/carbonetes-lens-extension.git 31 | ln -s $(pwd)/carbonetes-lens-extension ~/.k8slens/extensions/carbonetes-lens-extension 32 | ``` 33 | 34 | 2. To build the extension you can use `make` or run the `npm` commands manually: 35 | 36 | ```sh 37 | cd carbonetes-lens-extension 38 | make build 39 | ``` 40 | or 41 | ```sh 42 | cd carbonetes-lens-extension 43 | npm install 44 | npm run build 45 | ``` 46 | 3. Open Lens application and select **Lens** menu, and then click **Extensions** item, or press Shift + Command + E to open the **Manage Lens Extensions** page. 47 | 48 | ## Getting Started 49 | 50 | 1. You'll see the @carbonetes/carbonetes-lens-extension extension listed under Installed Extensions. Click Enable to enable it. 51 | ![](docs/images/manage-extension.png) 52 | 2. Image 53 | ![](docs/images/manage.png) 54 | 3. Complete Analysis 55 | ![](docs/images/complete-analysis.png) 56 | 4. Policy Evaluation 57 | ![](docs/images/policy-evaluation.png) 58 | 5. Vulnerabilities 59 | ![](docs/images/vulnerabilities.png) 60 | 61 | ## Uninstall 62 | 63 | ```sh 64 | rm ~/.k8slens/extensions/carbonetes-lens-extension 65 | ``` 66 | 67 | Restart Lens application. 68 | -------------------------------------------------------------------------------- /src/preferences/carbonetes-preference-store.ts: -------------------------------------------------------------------------------- 1 | import { Common } from "@k8slens/extensions"; 2 | import { observable, makeObservable, action } from "mobx"; 3 | 4 | // const { Store } = Common; 5 | 6 | export type CarbonetesStoreModel = { 7 | enabled: boolean; 8 | user: UserModel; 9 | analysis: AnalysisModel; 10 | analyses: AnalysesModel; 11 | registries: RegistriesModel; 12 | } 13 | 14 | export type UserModel = { 15 | email: string, 16 | password: string, 17 | auth: { 18 | username: string, 19 | token: string 20 | } 21 | } 22 | 23 | export type AnalysisModel = { 24 | deployment: any, 25 | result: any, 26 | isAnalyzing: boolean, 27 | isAnalyzed: boolean 28 | } 29 | 30 | export type AnalysesModel = AnalysisModel[] 31 | 32 | export type RegistriesModel = RegistriesModel[] 33 | 34 | 35 | export class CarbonetesStore extends Common.Store.ExtensionStore{ 36 | @observable enabled: boolean = false; 37 | 38 | @observable user: UserModel = { 39 | email: '', 40 | password: '', 41 | auth: { 42 | username: '', 43 | token: '' 44 | } 45 | } 46 | 47 | @observable analysis: AnalysisModel = { 48 | deployment: {}, 49 | result: {}, 50 | isAnalyzing: false, 51 | isAnalyzed: false, 52 | } 53 | 54 | @observable analyses: AnalysesModel = []; 55 | 56 | @observable registries: RegistriesModel = []; 57 | 58 | constructor() { 59 | super({ 60 | configName: "carbonetes-store", 61 | defaults: { 62 | enabled: false, 63 | user: null, 64 | analysis: { 65 | deployment: {}, 66 | result: {}, 67 | isAnalyzing: false, 68 | isAnalyzed: false, 69 | }, 70 | analyses: [], 71 | registries: [], 72 | } 73 | }); 74 | makeObservable(this); 75 | } 76 | 77 | // @action signIn(user: UserModel) { 78 | // this.enabled = true; 79 | // this.user = user; 80 | // }; 81 | 82 | // @action signOut() { 83 | // this.enabled = false; 84 | // this.resetUser(); 85 | // this.resetAnalysis(); 86 | // this.analyses = []; 87 | // this.registries = []; 88 | // }; 89 | 90 | // @action resetUser() { 91 | // this.user = { 92 | // email: '', 93 | // password: '', 94 | // auth: { 95 | // username: '', 96 | // token: '' 97 | // } 98 | // }; 99 | // } 100 | 101 | // @action resetAnalysis() { 102 | // this.analysis = { 103 | // deployment: {}, 104 | // result: {}, 105 | // isAnalyzing: false, 106 | // isAnalyzed: false, 107 | // }; 108 | // } 109 | 110 | protected fromStore({ enabled, user, analysis, analyses, registries }: CarbonetesStoreModel): void { 111 | this.enabled = enabled; 112 | this.user = user; 113 | this.analysis = analysis; 114 | this.analyses = analyses; 115 | this.registries = registries; 116 | } 117 | 118 | toJSON(): CarbonetesStoreModel { 119 | return { 120 | enabled: this.enabled, 121 | user: this.user, 122 | analysis: this.analysis, 123 | analyses: this.analyses, 124 | registries: this.registries 125 | }; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/details/vulnerabilities.tsx: -------------------------------------------------------------------------------- 1 | import { Renderer } from "@k8slens/extensions"; 2 | import React from "react"; 3 | import "./carbonetes-details.scss"; 4 | import { getSeverityStyle } from '../utils/helper'; 5 | 6 | const { Component } = Renderer; 7 | 8 | type Props = { 9 | vulnerabilities: [] 10 | } 11 | 12 | enum sortBy { 13 | vuln = "vuln", 14 | package_name = "package_name", 15 | severity = "severity", 16 | fix = "fix", 17 | } 18 | 19 | class Vulnerabilities extends React.PureComponent { 20 | 21 | private sortCallbacks = { 22 | [sortBy.vuln]: (vulnerability: any) => vulnerability.vuln, 23 | [sortBy.package_name]: (vulnerability: any) => vulnerability.package_name, 24 | [sortBy.severity]: (vulnerability: any) => vulnerability.severity, 25 | [sortBy.fix]: (vulnerability: any) => vulnerability.fix, 26 | }; 27 | 28 | render() { 29 | const { vulnerabilities } = this.props; 30 | 31 | 32 | if (vulnerabilities.length === 0) { 33 | return( 34 | <> 35 | ) 36 | } else { 37 | return ( 38 | <> 39 | 40 |
41 | 48 | 49 | ID 50 | Package Name 51 | Severity 52 | Fix 53 | 54 | { 55 | vulnerabilities.map((vulnerability: any) => { 56 | return ( 57 | 61 | 62 | {vulnerability.vuln} 63 | 64 | 65 | { 66 | vulnerability.package_name ? 67 | vulnerability.package_name 68 | : 69 | '_' 70 | } 71 | 72 | 73 | 77 | 78 | 79 | { 80 | vulnerability.fix ? 81 | vulnerability.fix 82 | : 83 | '_' 84 | } 85 | 86 | 87 | ) 88 | }) 89 | } 90 | 91 |
92 | 93 | ) 94 | } 95 | } 96 | } 97 | 98 | export default Vulnerabilities; -------------------------------------------------------------------------------- /src/details/policy-evaluation.tsx: -------------------------------------------------------------------------------- 1 | import { Renderer } from "@k8slens/extensions"; 2 | import React from "react"; 3 | import "./carbonetes-details.scss"; 4 | import { getStatusStyle } from '../utils/helper'; 5 | import { AnalysisModel } from '../preferences/carbonetes-preference-store'; 6 | 7 | const { Component } = Renderer; 8 | 9 | type Props = { 10 | analysis: AnalysisModel 11 | isAnalyzing: boolean 12 | } 13 | 14 | class PolicyEvaluation extends React.PureComponent { 15 | 16 | // Get the total per Policy Evaluation Action (Go, Warn, Stop) 17 | getPolicyEvaluationSummary = (results : []) => { 18 | let go = 0, 19 | warn = 0, 20 | stop = 0; 21 | 22 | results.forEach((result: any) => { 23 | const action = result.gateAction; 24 | 25 | switch (action) { 26 | case 'GO': 27 | go++; 28 | break; 29 | case 'WARN': 30 | warn++; 31 | break; 32 | case 'STOP': 33 | stop++; 34 | break; 35 | } 36 | }); 37 | 38 | return { 39 | go, 40 | warn, 41 | stop 42 | }; 43 | } 44 | 45 | render() { 46 | const { analysis, isAnalyzing } = this.props; 47 | const { isAnalyzed } = analysis; 48 | 49 | let policyEvaluation: any = {} 50 | if (isAnalyzed && (analysis.result && analysis.result.repoImageEnvironments && analysis.result.repoImageEnvironments.length > 0)) { 51 | policyEvaluation = { 52 | ...analysis.result.repoImageEnvironments[0], 53 | policyEvaluationSummary : this.getPolicyEvaluationSummary(analysis.result.repoImageEnvironments[0].policyEvaluationLatest.policyEvaluationResults) 54 | } 55 | } 56 | 57 | return ( 58 | <> 59 | 60 | 61 | { 62 | (policyEvaluation && policyEvaluation.environment) ? 63 | policyEvaluation.environment 64 | : 65 | isAnalyzing ? 66 | 67 | : 68 |

_

69 | } 70 |
71 | 72 | { 73 | (policyEvaluation && policyEvaluation.policyBundle) ? 74 | policyEvaluation.policyBundle.name 75 | : 76 | isAnalyzing ? 77 | 78 | : 79 |

_

80 | } 81 |
82 | 83 | { 84 | (policyEvaluation && policyEvaluation.policyEvaluationLatest) ? 85 | 90 | 91 | )} 92 | /> 93 | : 94 | isAnalyzing ? 95 | 96 | : 97 |

_

98 | } 99 |
100 | 101 | { 102 | (policyEvaluation && policyEvaluation.policyEvaluationLatest) ? 103 | 108 | 109 | )} 110 | /> 111 | : 112 | isAnalyzing ? 113 | 114 | : 115 |

_

116 | } 117 |
118 | 119 | { 120 | (policyEvaluation && policyEvaluation.policyEvaluationSummary) ? 121 |
122 | { 123 | Object.keys(policyEvaluation.policyEvaluationSummary).map((key: string) => { 124 | return ( 125 | 131 |

Evaluation

132 |
    133 |
  • Go - {policyEvaluation.policyEvaluationSummary.go}
  • 134 |
  • Warn - {policyEvaluation.policyEvaluationSummary.warn}
  • 135 |
  • Stop - {policyEvaluation.policyEvaluationSummary.stop}
  • 136 |
137 | 138 | )} 139 | /> 140 | ) 141 | }) 142 | } 143 |
144 | : 145 | isAnalyzing ? 146 | 147 | : 148 |

_

149 | } 150 |
151 | 152 | ) 153 | } 154 | } 155 | 156 | export default PolicyEvaluation; -------------------------------------------------------------------------------- /src/preferences/carbonetes-preferences.tsx: -------------------------------------------------------------------------------- 1 | import { Renderer } from "@k8slens/extensions"; 2 | import React from "react"; 3 | import { CarbonetesStore } from "./carbonetes-preference-store"; 4 | import request from '../service/requests'; 5 | import { observer } from "mobx-react"; 6 | import "./carbonetes-preferences.scss"; 7 | 8 | const { Component } = Renderer; 9 | 10 | type Props = {} 11 | 12 | type State = { 13 | email : string, 14 | password : string, 15 | isSigningIn : boolean, 16 | isSignInError : boolean 17 | } 18 | 19 | @observer 20 | export class CarbonetesPreferenceInput extends React.Component { 21 | constructor(props: { } | Readonly<{ }>) { 22 | super(props); 23 | this.state = { 24 | email : '', 25 | password : '', 26 | isSigningIn : false, 27 | isSignInError : false 28 | }; 29 | }; 30 | 31 | onChange = (value: string, event : React.ChangeEvent) => { 32 | this.setState({ 33 | [event.target.name]: value 34 | } as React.ComponentState); 35 | } 36 | 37 | signIn = () => { 38 | const { email, password } = this.state; 39 | 40 | if (email === '' || password === '') { 41 | this.setState({ 42 | isSignInError : true 43 | }); 44 | 45 | return; 46 | } 47 | 48 | this.setState({ 49 | isSigningIn : true 50 | }, () => { 51 | request.signIn({ 52 | username : email, 53 | password : password 54 | }).then((response: any) => { 55 | const user = { 56 | auth : response.data, 57 | email : email, 58 | password: password, 59 | }; 60 | 61 | CarbonetesStore.getInstance().enabled = true; 62 | CarbonetesStore.getInstance().user = user; 63 | 64 | request.getRegistries({ 65 | headers: { 66 | 'Authorization': `Bearer ${CarbonetesStore.getInstance().user.auth.token}` 67 | }, 68 | params: { 69 | email 70 | } 71 | }).then(response => { 72 | CarbonetesStore.getInstance().registries = response.data; 73 | }).catch(error => { 74 | Component.Notifications.error( 75 |
{error.response.data}.
76 | ) 77 | }); 78 | }).catch(error => { 79 | Component.Notifications.error( 80 |
Sorry, the credentials you entered is not valid.
81 | ) 82 | }).finally(() => { 83 | this.setState({ 84 | isSigningIn : false 85 | }); 86 | }); 87 | }); 88 | } 89 | 90 | signOut = () => { 91 | CarbonetesStore.getInstance().enabled = false; 92 | CarbonetesStore.getInstance().user = { 93 | email: '', 94 | password: '', 95 | auth: { 96 | username: '', 97 | token: '' 98 | } 99 | } 100 | CarbonetesStore.getInstance().analysis = { 101 | deployment: {}, 102 | result: {}, 103 | isAnalyzing: false, 104 | isAnalyzed: false, 105 | }; 106 | CarbonetesStore.getInstance().analyses = []; 107 | CarbonetesStore.getInstance().registries = []; 108 | 109 | this.setState({ 110 | email: '', 111 | password: '' 112 | }); 113 | } 114 | 115 | render() { 116 | const { email, isSignInError, isSigningIn, password } = this.state; 117 | 118 | return ( 119 |
120 | { 121 | CarbonetesStore.getInstance().enabled ? 122 | 123 | {`Currently signed in as ${!CarbonetesStore.getInstance().user?this.state.email:CarbonetesStore.getInstance().user.email}`} 124 | {/* {`Currently signed in as ${displayEmail}`} */} 125 | 126 | : 127 | <> 128 | 129 | Sign in to enable comprehensive image analysis using Carbonetes. 130 | 131 | 144 | 156 | 157 | } 158 |
159 |
173 | 174 | { 175 | !CarbonetesStore.getInstance().enabled && 176 | <>Don't have an account? Click here to register.
177 | } 178 | To know more about Carbonetes click here here. 179 |
180 |
181 | ) 182 | } 183 | } 184 | 185 | export class CarbonetesPreferenceHint extends React.Component { 186 | render() { 187 | return ( 188 | 189 | 190 | ) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/details/complete-analysis.tsx: -------------------------------------------------------------------------------- 1 | import { Renderer } from "@k8slens/extensions"; 2 | import React from "react"; 3 | import "./carbonetes-details.scss"; 4 | import { getSeverityStyle } from '../utils/helper'; 5 | import { AnalysisModel } from '../preferences/carbonetes-preference-store'; 6 | 7 | const { Component } = Renderer; 8 | 9 | type Props = { 10 | analysis: AnalysisModel, 11 | isAnalyzing: boolean 12 | } 13 | 14 | class CompleteAnalysis extends React.PureComponent { 15 | 16 | // Creates analysis object for table's row 17 | createAnalysis = (analyzer: string, summary: any): any => { 18 | const analysis = { 19 | analyzer, 20 | summary 21 | } 22 | 23 | return analysis 24 | } 25 | 26 | // Creates analyses based from response of getAnalysisResult API 27 | createAnalyses = (result: any) => { 28 | 29 | let analyses; 30 | if (result) { 31 | // Image Analysis 32 | const imageAnalysis = result.imageAnalysisLatest; 33 | const imageAnalysisSummary = imageAnalysis ? this.getVulnerabilitySummary(imageAnalysis.vulnerabilities) : null; 34 | 35 | // Software Composition Analysis 36 | const scAnalysis = result.scaLatest; 37 | const scAnalysisSummary = scAnalysis ? scAnalysis.analysis : null; 38 | 39 | // // Malware Analysis 40 | const malwareAnalysis = result.malwareAnalysisLatest; 41 | const malwareAnalysisSummary = malwareAnalysis ? malwareAnalysis.scanResult : null; 42 | 43 | // License Finder 44 | const licenseFinder = result.licenses; 45 | // const licenseFinderSummary = licenseFinder ? licenseFinder.imageDependencies.flatMap((dependency: any) => dependency.licenses).flatMap((license: any) => license.licenseName).filter((v: any, i: any, a: any) => a.indexOf(v) === i) : null; 46 | const licenseFinderSummary = licenseFinder ? licenseFinder.flatMap((license: any) => license.name).filter((v: any, i: any, a: any) => a.indexOf(v) === i) : null; 47 | 48 | // Secret Analysis 49 | const secretAnalysis = result.secretAnalysisLatest; 50 | const secretAnalysisSummary = secretAnalysis ? secretAnalysis.secrets : null; 51 | 52 | // Bill of Materials 53 | const bomAnalysis = result.billOfMaterialsAnalysisLatest; 54 | const bomAnalysisSummary = bomAnalysis ? bomAnalysis.artifacts : null; 55 | 56 | analyses = [ 57 | this.createAnalysis('Vulnerability', imageAnalysisSummary), 58 | this.createAnalysis('Software Composition', scAnalysisSummary), 59 | this.createAnalysis('Malware', malwareAnalysisSummary), 60 | this.createAnalysis('License', licenseFinderSummary), 61 | this.createAnalysis('Secrets', secretAnalysisSummary), 62 | this.createAnalysis('Bill Of Materials', bomAnalysisSummary) 63 | ]; 64 | } else { 65 | analyses = [ 66 | this.createAnalysis('Vulnerability', null), 67 | this.createAnalysis('Software Composition', null), 68 | this.createAnalysis('Malware', null), 69 | this.createAnalysis('License', null), 70 | this.createAnalysis('Secrets', null), 71 | this.createAnalysis('Bill Of Materials', null) 72 | ]; 73 | } 74 | 75 | return analyses; 76 | } 77 | 78 | // Get the total per Image Vulnerability (Critical, High, Medium, Low, Negligible, Unknown) 79 | getVulnerabilitySummary = (vulnerabilities : []) => { 80 | let critical = 0, 81 | high = 0, 82 | medium = 0, 83 | low = 0, 84 | negligible = 0, 85 | unknown = 0; 86 | 87 | vulnerabilities.forEach((vulnerability: any) => { 88 | const severity = vulnerability.severity.toLowerCase(); 89 | 90 | switch (severity) { 91 | case 'critical': 92 | critical++; 93 | break; 94 | case 'high': 95 | high++; 96 | break; 97 | case 'medium': 98 | medium++; 99 | break; 100 | case 'low': 101 | low++ 102 | break; 103 | case 'negligible': 104 | negligible++ 105 | break; 106 | default: 107 | unknown++ 108 | break; 109 | } 110 | }); 111 | 112 | return { 113 | critical, 114 | high, 115 | medium, 116 | low, 117 | negligible, 118 | unknown, 119 | }; 120 | } 121 | 122 | renderSummary = (analysis: any) => { 123 | const summary = analysis.summary; 124 | let component: any[] = []; 125 | 126 | if (!summary) { 127 | component.push(

_

) 128 | } else if (analysis.analyzer === 'Vulnerability' || analysis.analyzer === 'Software Composition') { 129 | let severity = summary; 130 | if (analysis.analyzer === 'Software Composition') { 131 | const { critical, high, medium, low } = summary 132 | 133 | severity = { 134 | critical, 135 | high, 136 | medium, 137 | low 138 | } 139 | } 140 | 141 | Object.keys(severity).map((key) => { 142 | if ((severity[key] != 0 || severity[key] != '')) { 143 | component.push( 144 | 150 | { 151 | analysis.analyzer === 'Software Composition' && 152 | <> 153 |

{`Total Dependencies: `}{summary.totalDependency}

154 |

{`Vulnerable Dependencies: `}{summary.vulnerableDependency}

155 |

{`Vulnerabilities Found: `}{summary.vulnerabilityFound}

156 |
157 | 158 | } 159 |

Severities

160 |
    161 |
  • Critical - {severity.critical}
  • 162 |
  • High - {severity.high}
  • 163 |
  • Medium - {severity.medium}
  • 164 |
  • Low - {severity.low}
  • 165 | { 166 | analysis.analyzer === 'Vulnerability' && 167 | <> 168 |
  • Negligible - {severity.negligible}
  • 169 |
  • Unknown - {severity.unknown}
  • 170 | 171 | } 172 |
173 | 174 | )} 175 | /> 176 | ) 177 | } 178 | }) 179 | 180 | component.length === 0 && component.push(

No vulnerabilities found

); 181 | } else if (analysis.analyzer === 'Malware') { 182 | const malwareCount = analysis.summary.infectedFiles.length; 183 | const noun = malwareCount > 1 ? 'threats' : 'threat'; 184 | 185 | if (malwareCount > 0) { 186 | component.push( 187 | 0 && 'threat'} 190 | tooltip={( 191 |
    192 |

    Threats

    193 | 194 | 195 | 196 | 197 | 198 | { 199 | analysis.summary.infectedFiles.map((infectedFile: any) => 200 | 201 | 202 | 203 | 204 | ) 205 | } 206 |
    FileVirus
    {infectedFile.file_name}{infectedFile.virus}
    207 |
208 | )} 209 | /> 210 | ) 211 | } else { 212 | component.push(

No threats found

); 213 | } 214 | } else if (analysis.analyzer === 'License') { 215 | const licenseCount = analysis.summary.length; 216 | const noun = licenseCount > 1 ? 'licenses' : 'license'; 217 | 218 | if (licenseCount > 0) { 219 | component.push( 220 | 0 && 'license'} 223 | tooltip={( 224 |
    225 |

    Licenses

    226 | { 227 | analysis.summary.map((license: any) => 228 |
  • {license}
  • 229 | ) 230 | } 231 |
232 | )} 233 | /> 234 | ) 235 | } else { 236 | component.push(

-

); 237 | } 238 | } else if (analysis.analyzer === 'Secrets') { 239 | const secretsCount = analysis.summary.length; 240 | if (secretsCount > 0) { 241 | const noun = secretsCount > 1 ? 'secrets' : 'secret'; 242 | component.push( 243 | 246 | ) 247 | } 248 | else { 249 | component.push(

No secrets found

); 250 | } 251 | component.length === 0 && component.push(

No secrets found

); 252 | } else if (analysis.analyzer === 'Bill Of Materials') { 253 | const artifactsCount = analysis.summary.length; 254 | 255 | if (artifactsCount > 0) { 256 | const noun = artifactsCount > 1 ? 'artifacts' : 'artifact'; 257 | const groupBy = (array: [], key: string) => { 258 | return array.reduce((result: any, currentValue) => { 259 | (result[currentValue[key]] = result[currentValue[key]]+1 || 1); 260 | return result; 261 | }, {}); 262 | }; 263 | const artifactCountByType = groupBy(analysis.summary, "type"); 264 | component.push( 265 | 0 && 'license'} 268 | tooltip={( 269 |
    270 |

    Artifact Types

    271 | { 272 | Object.keys(artifactCountByType).map(key => 273 |
  • {key}: {artifactCountByType[key]}
  • 274 | ) 275 | } 276 |
277 | )} 278 | /> 279 | ) 280 | } else { 281 | component.push(

No licenses found.

); 282 | } 283 | } 284 | 285 | return component; 286 | } 287 | 288 | render() { 289 | const { analysis, isAnalyzing } = this.props; 290 | const { result } = analysis; 291 | 292 | const analyses = this.createAnalyses(result); 293 | 294 | return ( 295 | <> 296 | 297 |
298 | 303 | 304 | Analyzers 305 | Summary 306 | 307 | { 308 | analyses.map((analysis: any) => { 309 | return ( 310 | 313 | 314 | { 315 | analysis.analyzer 316 | } 317 | 318 | 319 | { 320 | isAnalyzing ? 321 | 322 | : 323 | this.renderSummary(analysis) 324 | } 325 | 326 | 327 | ) 328 | }) 329 | } 330 | 331 |
332 | 333 | ) 334 | } 335 | } 336 | 337 | export default CompleteAnalysis; -------------------------------------------------------------------------------- /src/details/carbonetes-details.tsx: -------------------------------------------------------------------------------- 1 | import { Renderer } from "@k8slens/extensions"; 2 | import React from "react"; 3 | import moment from "moment"; 4 | import { CarbonetesStore } from "../preferences/carbonetes-preference-store"; 5 | import request from '../service/requests'; 6 | import PolicyEvaluation from './policy-evaluation'; 7 | import CompleteAnalysis from './complete-analysis'; 8 | import Vulnerabilities from './vulnerabilities'; 9 | import { 10 | REGISTRY, 11 | IMAGE_NAME 12 | } from '../utils/constants' 13 | import "./carbonetes-details.scss"; 14 | import { observer } from "mobx-react"; 15 | import { getAnalysisStatus, getStatusStyle } from "../utils/helper"; 16 | 17 | export interface CarbonetesDetailsProps extends Renderer.Component.KubeObjectDetailsProps { 18 | } 19 | 20 | type Props = { 21 | deployment : Renderer.Component.KubeObjectDetailsProps 22 | } 23 | 24 | type State = { 25 | isLoading : boolean, 26 | } 27 | 28 | @observer 29 | export class CarbonetesDetails extends React.Component { 30 | 31 | constructor(props: Props | Readonly) { 32 | super(props); 33 | 34 | this.state = { 35 | isLoading : false, 36 | }; 37 | }; 38 | 39 | componentDidMount() { 40 | const { deployment } = this.props; 41 | const { object } = deployment; 42 | const image = this.getImage(object); 43 | 44 | if (CarbonetesStore.getInstance().enabled && CarbonetesStore.getInstance().registries.find((registry: any) => registry.registryUri.includes(image.registry))) { 45 | this.getAnalysis(); 46 | } 47 | } 48 | 49 | componentDidUpdate(prevProps: any, prevState: any) { 50 | const { deployment } = this.props; 51 | const { object } = deployment; 52 | const image = this.getImage(object); 53 | 54 | // If Deployment metadata UID changes 55 | // Set initial state 56 | if (object.metadata.uid !== prevProps.deployment.object.metadata.uid) { 57 | if (CarbonetesStore.getInstance().enabled && CarbonetesStore.getInstance().registries.find((registry: any) => registry.registryUri.includes(image.registry))) { 58 | this.getAnalysis(); 59 | } 60 | } 61 | } 62 | 63 | getAnalysis = () => { 64 | const { deployment } = this.props; 65 | const { object } = deployment; 66 | 67 | if(CarbonetesStore.getInstance().analyses == null){ 68 | CarbonetesStore.getInstance().analyses = []; 69 | CarbonetesStore.getInstance().analysis = { 70 | deployment: {}, 71 | result: {}, 72 | isAnalyzing: false, 73 | isAnalyzed: false, 74 | }; 75 | CarbonetesStore.getInstance().analysis.deployment = object; 76 | this.checkAnalysisResult(); 77 | return 78 | } 79 | 80 | const currentAnalysis = CarbonetesStore.getInstance().analyses.filter((analysis: any) => analysis.deployment.metadata.uid === object.metadata.uid); 81 | if (currentAnalysis.length > 0) { 82 | const analyzed = currentAnalysis.find((analysis: any) => analysis.isAnalyzed === true); 83 | const analyzing = currentAnalysis.find((analysis: any) => analysis.isAnalyzed === false); 84 | if (analyzed) { 85 | CarbonetesStore.getInstance().analysis = analyzed; 86 | } else { 87 | CarbonetesStore.getInstance().analysis = analyzing; 88 | } 89 | } else { 90 | CarbonetesStore.getInstance().analysis = { 91 | deployment: {}, 92 | result: {}, 93 | isAnalyzing: false, 94 | isAnalyzed: false, 95 | }; 96 | CarbonetesStore.getInstance().analysis.deployment = object; 97 | this.checkAnalysisResult(); 98 | } 99 | } 100 | 101 | // Get image registry and image tag from KubeObjectDetailsProps 102 | getImage: any = (deployment: Renderer.K8sApi.Deployment, property: String = null) => { 103 | const containers = deployment.spec.template.spec.containers; 104 | 105 | let image = { 106 | registry: '', 107 | name: '', 108 | }; 109 | 110 | if (containers.length > 0) { 111 | const imageArray = containers[0].image.split(/\/(.+)/); 112 | image.registry= imageArray[0]; 113 | image.name = imageArray[1]; 114 | 115 | // containers.map((container) => { 116 | // // Split the string that matches with the first '/' 117 | // const imageArray = container.image.split(/\/(.+)/); 118 | 119 | // image.registry= imageArray[0]; 120 | // image.name = imageArray[1]; 121 | // }); 122 | } 123 | 124 | const { registry, name } = image; 125 | 126 | switch (property) { 127 | case REGISTRY: 128 | return registry; 129 | case IMAGE_NAME: 130 | return name; 131 | default: 132 | return image 133 | } 134 | } 135 | 136 | // Check image if image is already been analyzed 137 | checkAnalysisResult = () => { 138 | const { deployment } = this.props; 139 | const { object } = deployment; 140 | 141 | const image = this.getImage(object); 142 | const imageName = image.name.split(':')[0]; 143 | const imageTag = image.name.split(':')[1]; 144 | 145 | const params = { 146 | registryUri : image.registry, 147 | repoImageTag : image.name, 148 | username : CarbonetesStore.getInstance().user.email, 149 | password : CarbonetesStore.getInstance().user.password, 150 | timeout : 2700000, 151 | policyBundleUUID : '' 152 | } 153 | 154 | request.getAndReloadCompayRegistry({params}).then(response => { 155 | if (response.data) { 156 | CarbonetesStore.getInstance().analysis.isAnalyzing = true; 157 | 158 | request.checkAnalysisResult({ 159 | // headers: { 160 | // 'Authorization': `Bearer ${CarbonetesStore.getInstance().user.auth.token}` 161 | // }, 162 | params: { 163 | registryUri : image.registry, 164 | repo : imageName, 165 | tag : imageTag, 166 | username : CarbonetesStore.getInstance().user.email, 167 | password : CarbonetesStore.getInstance().user.password 168 | } 169 | }).then(response => { 170 | const result = response.data; 171 | 172 | CarbonetesStore.getInstance().analysis.deployment = object; 173 | CarbonetesStore.getInstance().analysis.result = result; 174 | CarbonetesStore.getInstance().analysis.isAnalyzed = true; 175 | CarbonetesStore.getInstance().analysis.isAnalyzing = false; 176 | 177 | const newAnalyses = CarbonetesStore.getInstance().analyses.filter((analysis) => (analysis.result.imageDigest !== result.imageDigest || analysis.deployment.metadata.uid !== object.metadata.uid)); 178 | 179 | newAnalyses.push({ 180 | deployment : object, 181 | result : result, 182 | isAnalyzing : false, 183 | isAnalyzed : true 184 | }); 185 | 186 | CarbonetesStore.getInstance().analyses = newAnalyses; 187 | }).catch(error => { 188 | Renderer.Component.Notifications.error( 189 |
{error.response.data}.
190 | ) 191 | CarbonetesStore.getInstance().analysis = { 192 | deployment: {}, 193 | result: {}, 194 | isAnalyzing: false, 195 | isAnalyzed: false, 196 | }; 197 | }); 198 | } 199 | }).catch(error => { 200 | Renderer.Component.Notifications.error( 201 |
{error.response.data}.
202 | ) 203 | }); 204 | } 205 | 206 | // Fetches analysis result in Carbonetes API 207 | getAnalysisResult = (param: {}) => { 208 | const { deployment } = this.props; 209 | const { object } = deployment; 210 | 211 | request.getAnalysisResult({ 212 | ...param 213 | }).then(response => { 214 | const result = response.data 215 | const newAnalyses = CarbonetesStore.getInstance().analyses.filter((analysis) => (analysis.result.imageDigest !== result.imageDigest || analysis.deployment.metadata.uid !== object.metadata.uid)); 216 | 217 | newAnalyses.push({ 218 | deployment : object, 219 | result : result, 220 | isAnalyzing : false, 221 | isAnalyzed : true 222 | }) 223 | 224 | CarbonetesStore.getInstance().analysis.result = result; 225 | CarbonetesStore.getInstance().analysis.isAnalyzed = true; 226 | CarbonetesStore.getInstance().analyses = newAnalyses; 227 | 228 | }).catch(error => { 229 | Renderer.Component.Notifications.error( 230 |
{error.response.data}.
231 | ) 232 | }).finally(() => { 233 | CarbonetesStore.getInstance().analysis.isAnalyzing = false; 234 | }); 235 | } 236 | 237 | // Analyzes image using Carbonetes API 238 | analyzeImage = () => { 239 | const { deployment } = this.props; 240 | const { object } = deployment; 241 | 242 | const image = this.getImage(object); 243 | const newAnalyses = CarbonetesStore.getInstance().analyses.filter((analysis) => analysis.deployment.metadata.uid !== object.metadata.uid); 244 | 245 | newAnalyses.push({ 246 | deployment : object, 247 | result : {}, 248 | isAnalyzing : true, 249 | isAnalyzed : false 250 | }); 251 | 252 | CarbonetesStore.getInstance().analysis.deployment = object; 253 | CarbonetesStore.getInstance().analysis.result = {}; 254 | CarbonetesStore.getInstance().analysis.isAnalyzing = true; 255 | CarbonetesStore.getInstance().analysis.isAnalyzed = false; 256 | // CarbonetesStore.getInstance().analyses = newAnalyses; 257 | 258 | request.analyzeImage({ 259 | registryUri : image.registry, 260 | repoImageTag : image.name, 261 | username : CarbonetesStore.getInstance().user.email, 262 | password : CarbonetesStore.getInstance().user.password, 263 | timeout : 2700000, 264 | policyBundleUUID : '' 265 | }).then(response => { 266 | CarbonetesStore.getInstance().analyses = newAnalyses; 267 | 268 | this.getAnalysisResult(response.data); 269 | }).catch(error => { 270 | Renderer.Component.Notifications.error( 271 |
{error.response.data}.
272 | ) 273 | CarbonetesStore.getInstance().analysis = { 274 | deployment: {}, 275 | result: {}, 276 | isAnalyzing: false, 277 | isAnalyzed: false, 278 | }; 279 | }); 280 | } 281 | 282 | render() { 283 | const { deployment } = this.props; 284 | const { object } = deployment; 285 | const image = this.getImage(object); 286 | 287 | if (CarbonetesStore.getInstance().enabled && CarbonetesStore.getInstance().registries.find((registry: any) => registry.registryUri.includes(image.registry))) { 288 | if(!CarbonetesStore.getInstance().analysis){ 289 | return (

No Analysis

) 290 | } 291 | return( 292 |
293 | 294 | 295 | {image.registry} 296 | 297 | 298 | {image.name} 299 | 300 | 301 | { 302 | CarbonetesStore.getInstance().analysis.result && CarbonetesStore.getInstance().analysis.result.comprehensiveAnalysisLatest ? 303 | moment.unix(CarbonetesStore.getInstance().analysis.result.comprehensiveAnalysisLatest.whenAdded).fromNow() 304 | : 305 |

_

306 | } 307 |
308 | 309 | { 310 | CarbonetesStore.getInstance().analysis.result && CarbonetesStore.getInstance().analysis.result.comprehensiveAnalysisLatest ? 311 | 315 | : 316 |

_

317 | } 318 |
319 | 326 | 327 | 328 | 336 |
337 | ) 338 | } else { 339 | return ( 340 | <> 341 | ) 342 | } 343 | } 344 | } --------------------------------------------------------------------------------