From e46701e2cd15e3ed4983de32078983fbd8ff8a62 Mon Sep 17 00:00:00 2001 From: Free Wortley Date: Thu, 16 Feb 2023 16:59:01 -0800 Subject: [PATCH 1/5] One more comment --- .../resolvers/create-pull-request-for-vulnerability.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts index 743589d98..1e9abb4ae 100644 --- a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts +++ b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts @@ -26,6 +26,7 @@ import { checkProjectIsAuthorizedOrThrow, isAuthenticated } from '../helpers/aut type CreatePullRequestForVulnerabilityType = NonNullable; function splitGitHubRepoPath(gitHubRepo: string) { + // URL from GitHub of the repo, eg: git://github.com/lunasec-io/lunasec.git const split = gitHubRepo.split('/'); const owner = split[split.length - 2]; const repo = split[split.length - 1].replace('.git', ''); From 00ee8329b0b1c0dd4ba84d8cbb7dfd1a574d7839 Mon Sep 17 00:00:00 2001 From: Free Wortley Date: Thu, 16 Feb 2023 18:17:16 -0800 Subject: [PATCH 2/5] Front-End for GitHub PR Generation This is tested and it works, but we still need to write the final part of the UI. (Showing the link to the PR that was created.) For now, this does work and it just needs some additional polish to be shipped. We will need to increase the permissions that we ask for with the GitHub app, unfortunately, because we don't currently have write permissions to repositories associated with the GitHub App. Maybe there is a way that we can progressively do that to let people opt-in? That's a discussion for later. --- .../create-pull-request-for-vulnerability.ts | 2 + lunatrace/bsl/frontend/src/api/generated.ts | 51 +++++++++++++++++-- ...tateCreateGitHubPullRequestForVuln.graphql | 18 +++++++ .../details/VulnerablePackageListWrapper.tsx | 4 +- .../PackageUpdatablePopOver.tsx | 47 ++++++++++++----- .../VulnerablePackageCardHeader.tsx | 4 +- .../VulnerablePackageMain.tsx | 26 ++++++++-- .../src/package/github-pr/index.ts | 2 +- 8 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 lunatrace/bsl/frontend/src/api/graphql/mutateCreateGitHubPullRequestForVuln.graphql diff --git a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts index 1e9abb4ae..8bf8e6769 100644 --- a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts +++ b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts @@ -103,6 +103,8 @@ export const createPullRequestForVulnerabilityResolver: CreatePullRequestForVuln const { owner, repo } = splitGitHubRepoPath(githubRepo); + console.log('installation id', installationId); + const installationAccessTokenRes = await getInstallationAccessToken(installationId); if (installationAccessTokenRes.error) { log.error('Failed to fetch installation access token', { installationAccessTokenRes }); diff --git a/lunatrace/bsl/frontend/src/api/generated.ts b/lunatrace/bsl/frontend/src/api/generated.ts index 8965da27d..7c9137727 100644 --- a/lunatrace/bsl/frontend/src/api/generated.ts +++ b/lunatrace/bsl/frontend/src/api/generated.ts @@ -171,6 +171,12 @@ export type BuildData_VulnerableRelease = { trivially_updatable: Scalars['String']; }; +export type CreatePullRequestForVulnerabilityResponse = { + __typename?: 'CreatePullRequestForVulnerabilityResponse'; + pullRequestUrl: Scalars['String']; + success?: Maybe; +}; + /** Boolean expression to compare columns of type "Float". All fields are combined with logical 'AND'. */ export type Float_Comparison_Exp = { _eq?: InputMaybe; @@ -3760,6 +3766,7 @@ export enum Manifests_Update_Column { /** mutation root */ export type Mutation_Root = { __typename?: 'mutation_root'; + createPullRequestForVulnerability?: Maybe; /** delete data from the table: "builds" */ delete_builds?: Maybe; /** delete single row from the table: "builds" */ @@ -3854,6 +3861,16 @@ export type Mutation_Root = { }; +/** mutation root */ +export type Mutation_RootCreatePullRequestForVulnerabilityArgs = { + new_package_slug: Scalars['String']; + old_package_slug: Scalars['String']; + package_manifest_path: Scalars['String']; + project_id: Scalars['uuid']; + vulnerability_id: Scalars['uuid']; +}; + + /** mutation root */ export type Mutation_RootDelete_BuildsArgs = { where: Builds_Bool_Exp; @@ -6330,7 +6347,7 @@ export type Query_RootVulnerability_Cisa_Known_ExploitedArgs = { export type Query_RootVulnerability_Cisa_Known_Exploited_By_PkArgs = { - id: Scalars['uuid']; + cve: Scalars['String']; }; @@ -7681,7 +7698,7 @@ export type Subscription_RootVulnerability_Cisa_Known_ExploitedArgs = { export type Subscription_RootVulnerability_Cisa_Known_Exploited_By_PkArgs = { - id: Scalars['uuid']; + cve: Scalars['String']; }; @@ -8429,7 +8446,7 @@ export type Vulnerability_Bool_Exp = { /** columns and relationships of "vulnerability.cisa_known_exploited" */ export type Vulnerability_Cisa_Known_Exploited = { __typename?: 'vulnerability_cisa_known_exploited'; - cve?: Maybe; + cve: Scalars['String']; date_added: Scalars['date']; due_date: Scalars['date']; id: Scalars['uuid']; @@ -9349,6 +9366,17 @@ export type InsertProjectAccessTokenMutationVariables = Exact<{ export type InsertProjectAccessTokenMutation = { __typename?: 'mutation_root', insert_project_access_tokens_one?: { __typename?: 'project_access_tokens', id: any } | null }; +export type CreateGitHubPullRequestForVulnMutationVariables = Exact<{ + project_id: Scalars['uuid']; + vulnerability_id: Scalars['uuid']; + old_package_slug: Scalars['String']; + new_package_slug: Scalars['String']; + package_manifest_path: Scalars['String']; +}>; + + +export type CreateGitHubPullRequestForVulnMutation = { __typename?: 'mutation_root', createPullRequestForVulnerability?: { __typename?: 'CreatePullRequestForVulnerabilityResponse', success?: boolean | null, pullRequestUrl: string } | null }; + export type InsertIgnoredVulnerabilitiesMutationVariables = Exact<{ objects: Array | Ignored_Vulnerabilities_Insert_Input; }>; @@ -10308,6 +10336,20 @@ export const InsertProjectAccessTokenDocument = ` } } `; +export const CreateGitHubPullRequestForVulnDocument = ` + mutation CreateGitHubPullRequestForVuln($project_id: uuid!, $vulnerability_id: uuid!, $old_package_slug: String!, $new_package_slug: String!, $package_manifest_path: String!) { + createPullRequestForVulnerability( + project_id: $project_id + vulnerability_id: $vulnerability_id + old_package_slug: $old_package_slug + new_package_slug: $new_package_slug + package_manifest_path: $package_manifest_path + ) { + success + pullRequestUrl + } +} + `; export const InsertIgnoredVulnerabilitiesDocument = ` mutation InsertIgnoredVulnerabilities($objects: [ignored_vulnerabilities_insert_input!]!) { insert_ignored_vulnerabilities( @@ -10500,6 +10542,9 @@ const injectedRtkApi = api.injectEndpoints({ InsertProjectAccessToken: build.mutation({ query: (variables) => ({ document: InsertProjectAccessTokenDocument, variables }) }), + CreateGitHubPullRequestForVuln: build.mutation({ + query: (variables) => ({ document: CreateGitHubPullRequestForVulnDocument, variables }) + }), InsertIgnoredVulnerabilities: build.mutation({ query: (variables) => ({ document: InsertIgnoredVulnerabilitiesDocument, variables }) }), diff --git a/lunatrace/bsl/frontend/src/api/graphql/mutateCreateGitHubPullRequestForVuln.graphql b/lunatrace/bsl/frontend/src/api/graphql/mutateCreateGitHubPullRequestForVuln.graphql new file mode 100644 index 000000000..1904ec2ee --- /dev/null +++ b/lunatrace/bsl/frontend/src/api/graphql/mutateCreateGitHubPullRequestForVuln.graphql @@ -0,0 +1,18 @@ +mutation CreateGitHubPullRequestForVuln( + $project_id: uuid!, + $vulnerability_id: uuid!, + $old_package_slug: String!, + $new_package_slug: String!, + $package_manifest_path: String! +) { + createPullRequestForVulnerability( + project_id: $project_id, + vulnerability_id: $vulnerability_id, + old_package_slug: $old_package_slug, + new_package_slug: $new_package_slug, + package_manifest_path: $package_manifest_path + ) { + success + pullRequestUrl + } +} diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/VulnerablePackageListWrapper.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/VulnerablePackageListWrapper.tsx index 769f995ee..3dd0a7982 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/VulnerablePackageListWrapper.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/VulnerablePackageListWrapper.tsx @@ -44,10 +44,10 @@ export const VulnerablePackageListWrapper: React.FC { - // severity state for modern tree data, legacy has its own state and doesnt use this + // severity state for modern tree data, legacy has its own state and doesn't use this const [severity, setSeverity] = useState('Critical'); - // data for modern tree, legacy doesnt use this + // data for modern tree, legacy doesn't use this const { data: vulnerableReleasesData, isLoading, diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx index e680b063e..4b3fd0db9 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx @@ -12,14 +12,19 @@ * */ import React from 'react'; -import { NavLink, OverlayTrigger, Popover, Tooltip } from 'react-bootstrap'; +import { Button, NavLink, OverlayTrigger, Popover, Tooltip } from 'react-bootstrap'; import { CopyBlock, tomorrowNight } from 'react-code-blocks'; +import { BsGithub } from 'react-icons/bs'; import { FcUpload } from 'react-icons/fc'; import useBreakpoint from '../../../../../../hooks/useBreakpoint'; import { isDirectDep } from '../../../../../../utils/package'; import { VulnerablePackage } from '../types'; -export const PackageUpdatablePopOver: React.FC<{ pkg: VulnerablePackage }> = ({ pkg }) => { + +export const PackageUpdatablePopOver: React.FC<{ + pkg: VulnerablePackage; + onClickUpdate: (pkg: VulnerablePackage) => void; +}> = ({ pkg, onClickUpdate }) => { const trivialUpdateStatus = pkg.trivially_updatable; if (!trivialUpdateStatus || trivialUpdateStatus === 'no') { @@ -30,13 +35,21 @@ export const PackageUpdatablePopOver: React.FC<{ pkg: VulnerablePackage }> = ({ return ( Trivially Updatable - - A fix is available within the semver range this package was requested with, meaning that the{' '} - project lockfile is probably the only thing constraining the package to the vulnerable - version. -
+ +
+ +
+ Clicking this button will open a Pull Request to update the project's lockfile. +
+ <> + A fix is available within the semver range this package was requested with, meaning that the{' '} + project lockfile is likely constraining the package to the vulnerable version. + +
{isDirectDep(pkg) ? ( - <> +
This command will update the package: = ({ theme={tomorrowNight} codeBlock /> - +
) : ( - <> +
This package is a transitive (deep) dependency. Due to constraints of NPM and Yarn, - updating it is only possible by manually deleting it from your project's lockfile (or deleting your - entire lockfile) and then re-running your package install command (npm install) to fetch the latest - version. Automated lockfile patching is currently in development by LunaTrace. - + updating it is only possible using a special tool like{' '} + + @lunatrace/npm-package-cli + {' '} + (which powers the automated patcher above). +
)}
diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx index 27710faa2..d0017ea35 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx @@ -26,11 +26,13 @@ import { PackageUpdatablePopOver } from './PackageUpdatablePopOver'; interface VulnerablePackageCardHeaderProps { ignored: boolean; pkg: VulnerablePackage; + onClickUpdate: (pkg: VulnerablePackage) => void; } export const VulnerablePackageCardHeader: React.FunctionComponent = ({ pkg, ignored, + onClickUpdate, }) => { const recommendedVersion = semver.rsort([...pkg.fix_versions])[0]; return ( @@ -50,7 +52,7 @@ export const VulnerablePackageCardHeader: React.FunctionComponent )} - + { const [showConfirmation, setShowConfirmation] = useState(false); const [insertVulnIgnore, insertVulnIgnoreState] = api.useInsertIgnoredVulnerabilitiesMutation(); + const [createGitHubPullRequestForVuln] = api.useCreateGitHubPullRequestForVulnMutation(); + const [ignoreNote, setIgnoreNote] = useState(''); const { project_id } = useParams(); const [shouldFilterFindingsBySeverity, setShouldFilterFindingsBySeverity] = useState(true); @@ -65,6 +68,8 @@ export const VulnerablePackageMain: React.FunctionComponent f.ignored); + const recommendedVersion = semver.rsort([...pkg.fix_versions])[0]; + const bulkIgnoreVulns = async () => { if (!project_id) { throw new Error('attempted to ignore a vuln but no project id is in the url'); @@ -82,7 +87,7 @@ export const VulnerablePackageMain: React.FunctionComponent) => void,children:React.ReactNode } + { onClick: (e: React.MouseEvent) => void; children: React.ReactNode } >(({ children, onClick }, ref) => ( {renderIgnoreUi()} - + Date: Thu, 16 Feb 2023 18:24:54 -0800 Subject: [PATCH 3/5] Cleanup --- .../resolvers/create-pull-request-for-vulnerability.ts | 2 -- .../vulnerable-package-card/VulnerablePackageMain.tsx | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts index 8bf8e6769..1e9abb4ae 100644 --- a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts +++ b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts @@ -103,8 +103,6 @@ export const createPullRequestForVulnerabilityResolver: CreatePullRequestForVuln const { owner, repo } = splitGitHubRepoPath(githubRepo); - console.log('installation id', installationId); - const installationAccessTokenRes = await getInstallationAccessToken(installationId); if (installationAccessTokenRes.error) { log.error('Failed to fetch installation access token', { installationAccessTokenRes }); diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx index 6db5d55b2..090b44bdf 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx @@ -118,9 +118,7 @@ export const VulnerablePackageMain: React.FunctionComponent Date: Fri, 3 Mar 2023 15:41:08 -0800 Subject: [PATCH 4/5] Finish up the UI, fix some bugs --- .../PackageUpdatablePopOver.tsx | 29 ++++++++++++++++--- .../VulnerablePackageCardHeader.tsx | 2 +- .../VulnerablePackageMain.tsx | 18 ++++++++++-- .../src/package/github-pr/index.ts | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx index 4b3fd0db9..f5675404d 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/PackageUpdatablePopOver.tsx @@ -23,8 +23,11 @@ import { VulnerablePackage } from '../types'; export const PackageUpdatablePopOver: React.FC<{ pkg: VulnerablePackage; - onClickUpdate: (pkg: VulnerablePackage) => void; + onClickUpdate: (pkg: VulnerablePackage) => Promise; }> = ({ pkg, onClickUpdate }) => { + const [creatingPr, setCreatingPr] = React.useState(false); + const [showPopOver, setShowPopOver] = React.useState(false); + const trivialUpdateStatus = pkg.trivially_updatable; if (!trivialUpdateStatus || trivialUpdateStatus === 'no') { @@ -37,8 +40,19 @@ export const PackageUpdatablePopOver: React.FC<{ Trivially Updatable
-
Clicking this button will open a Pull Request to update the project's lockfile. @@ -92,7 +106,14 @@ export const PackageUpdatablePopOver: React.FC<{ return ( <> {' '} - + setShowPopOver(nextShow)} + > {trivialUpdateStatus === 'partially' ? 'partially ' : ''}updatable diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx index d0017ea35..dd30abf4c 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageCardHeader.tsx @@ -26,7 +26,7 @@ import { PackageUpdatablePopOver } from './PackageUpdatablePopOver'; interface VulnerablePackageCardHeaderProps { ignored: boolean; pkg: VulnerablePackage; - onClickUpdate: (pkg: VulnerablePackage) => void; + onClickUpdate: (pkg: VulnerablePackage) => Promise; } export const VulnerablePackageCardHeader: React.FunctionComponent = ({ diff --git a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx index 090b44bdf..17fe970e1 100644 --- a/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx +++ b/lunatrace/bsl/frontend/src/pages/project/builds/details/vulnerable-packages/vulnerable-package-card/VulnerablePackageMain.tsx @@ -49,6 +49,8 @@ export const VulnerablePackageMain: React.FunctionComponent { return !affectedByVuln.beneath_minimum_severity || !shouldFilterFindingsBySeverity; }); @@ -128,8 +130,12 @@ export const VulnerablePackageMain: React.FunctionComponent {renderIgnoreUi()} + {patchPullRequestUrl && ( +
+ )} Date: Fri, 3 Mar 2023 16:05:44 -0800 Subject: [PATCH 5/5] Attempt to fix CI failure --- .github/workflows/lunatrace-stack-and-hasura.yaml | 1 + .../resolvers/create-pull-request-for-vulnerability.ts | 2 +- lunatrace/npm-package-cli/package.json | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lunatrace-stack-and-hasura.yaml b/.github/workflows/lunatrace-stack-and-hasura.yaml index b0d9df9b3..4dfadc851 100644 --- a/.github/workflows/lunatrace-stack-and-hasura.yaml +++ b/.github/workflows/lunatrace-stack-and-hasura.yaml @@ -36,6 +36,7 @@ jobs: - name: Build run: |- set -x + (cd lunatrace/npm-package-cli && yarn run compile) (cd lunatrace/bsl/logger && yarn run compile) (cd lunatrace/bsl/common && yarn run compile) (cd lunatrace/bsl/backend && yarn run compile) diff --git a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts index 1e9abb4ae..326ad7020 100644 --- a/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts +++ b/lunatrace/bsl/backend/src/graphql-yoga/resolvers/create-pull-request-for-vulnerability.ts @@ -15,7 +15,7 @@ import { GraphQLYogaError } from '@graphql-yoga/node'; import { PullRequestOctokit, replacePackageAndFileGitHubPullRequest, -} from '@lunatrace/npm-package-cli/src/package/github-pr'; +} from '@lunatrace/npm-package-cli/src/package/github-pr/index'; import { getInstallationAccessToken } from '../../github/auth'; import { hasura } from '../../hasura-api'; diff --git a/lunatrace/npm-package-cli/package.json b/lunatrace/npm-package-cli/package.json index c3100d0ac..4e7f9e655 100644 --- a/lunatrace/npm-package-cli/package.json +++ b/lunatrace/npm-package-cli/package.json @@ -69,6 +69,7 @@ }, "scripts": { "build": "shx rm -rf dist && tsc -b", + "compile": "yarn build", "dev": "node ./bin/dev", "dev-debug": "node --inspect-brk ./bin/dev", "postpack": "shx rm -f oclif.manifest.json",