diff --git a/dist/index.js b/dist/index.js index 70c6fc6..f66616d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -13401,33 +13401,80 @@ function warnOtherCommits() { "Try using '@dependabot rebase' to remove merge commits or '@dependabot recreate' to remove " + 'any non-Dependabot changes.'); } +function getAlert(name, version, directory, client, context) { + var _a, _b, _c, _d, _e; + return __awaiter(this, void 0, void 0, function* () { + const alerts = yield client.graphql(` + { + repository(owner: "${context.repo.owner}", name: "${context.repo.repo}") { + vulnerabilityAlerts(first: 100) { + nodes { + vulnerableManifestFilename + vulnerableManifestPath + vulnerableRequirements + state + securityVulnerability { + package { name } + } + securityAdvisory { + cvss { score } + ghsaId + } + } + } + } + }`); + const nodes = (_b = (_a = alerts === null || alerts === void 0 ? void 0 : alerts.repository) === null || _a === void 0 ? void 0 : _a.vulnerabilityAlerts) === null || _b === void 0 ? void 0 : _b.nodes; + const found = nodes.find(a => a.vulnerableRequirements === `= ${version}` && + trimSlashes(a.vulnerableManifestPath) === `${trimSlashes(directory)}/${a.vulnerableManifestFilename}` && + a.securityVulnerability.package.name === name); + return { + alertState: (_c = found === null || found === void 0 ? void 0 : found.state) !== null && _c !== void 0 ? _c : '', + ghsaId: (_d = found === null || found === void 0 ? void 0 : found.securityAdvisory.ghsaId) !== null && _d !== void 0 ? _d : '', + cvss: (_e = found === null || found === void 0 ? void 0 : found.securityAdvisory.cvss.score) !== null && _e !== void 0 ? _e : 0.0 + }; + }); +} +function trimSlashes(value) { + return value.replace(/^\//, '').replace(/\/$/, ''); +} // EXTERNAL MODULE: ./node_modules/yaml/index.js var yaml = __nccwpck_require__(3552); ;// CONCATENATED MODULE: ./src/dependabot/update_metadata.ts +var update_metadata_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; -function parse(commitMessage, branchName, mainBranch) { - const yamlFragment = commitMessage.match(/^-{3}\n(?[\S|\s]*?)\n^\.{3}\n/m); - if ((yamlFragment === null || yamlFragment === void 0 ? void 0 : yamlFragment.groups) && branchName.startsWith('dependabot')) { - const data = yaml.parse(yamlFragment.groups.dependencies); - // Since we are on the `dependabot` branch (9 letters), the 10th letter in the branch name is the delimiter - const delim = branchName[10]; - const chunks = branchName.split(delim); - const dirname = chunks.slice(2, -1).join(delim) || '/'; - if (data['updated-dependencies']) { - return data['updated-dependencies'].map(dependency => { - return { - dependencyName: dependency['dependency-name'], - dependencyType: dependency['dependency-type'], - updateType: dependency['update-type'], - directory: dirname, - packageEcosystem: chunks[1], - targetBranch: mainBranch - }; - }); +function parse(commitMessage, branchName, mainBranch, lookup) { + var _a, _b, _c, _d, _e, _f; + return update_metadata_awaiter(this, void 0, void 0, function* () { + const firstLine = commitMessage.split('\n')[0]; + const directory = firstLine.match(/ in (?\/[^ ]*)$/); + const bumpFragment = commitMessage.match(/^Bumps .* from (?\d[^ ]*) to (?\d[^ ]*)\.$/m); + const yamlFragment = commitMessage.match(/^-{3}\n(?[\S|\s]*?)\n^\.{3}\n/m); + if ((yamlFragment === null || yamlFragment === void 0 ? void 0 : yamlFragment.groups) && branchName.startsWith('dependabot')) { + const data = yaml.parse(yamlFragment.groups.dependencies); + // Since we are on the `dependabot` branch (9 letters), the 10th letter in the branch name is the delimiter + const delim = branchName[10]; + const chunks = branchName.split(delim); + const dirname = (_b = (_a = directory === null || directory === void 0 ? void 0 : directory.groups) === null || _a === void 0 ? void 0 : _a.directory) !== null && _b !== void 0 ? _b : '/'; + const prev = (_d = (_c = bumpFragment === null || bumpFragment === void 0 ? void 0 : bumpFragment.groups) === null || _c === void 0 ? void 0 : _c.from) !== null && _d !== void 0 ? _d : ''; + const next = (_f = (_e = bumpFragment === null || bumpFragment === void 0 ? void 0 : bumpFragment.groups) === null || _e === void 0 ? void 0 : _e.to) !== null && _f !== void 0 ? _f : ''; + if (data['updated-dependencies']) { + return yield Promise.all(data['updated-dependencies'].map((dependency) => update_metadata_awaiter(this, void 0, void 0, function* () { + return (Object.assign({ dependencyName: dependency['dependency-name'], dependencyType: dependency['dependency-type'], updateType: dependency['update-type'], directory: dirname, packageEcosystem: chunks[1], targetBranch: mainBranch, prevVersion: prev, newVersion: next }, yield lookup(dependency['dependency-name'], prev, dirname))); + }))); + } } - } - return []; + return Promise.resolve([]); + }); } // EXTERNAL MODULE: ./node_modules/pluralize/pluralize.js @@ -13456,6 +13503,11 @@ function set(updatedDependencies) { const directory = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.directory; const ecosystem = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.packageEcosystem; const target = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.targetBranch; + const prevVersion = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.prevVersion; + const newVersion = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.newVersion; + const alertState = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.alertState; + const ghsaId = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.ghsaId; + const cvss = firstDependency === null || firstDependency === void 0 ? void 0 : firstDependency.cvss; core.startGroup(`Outputting metadata for ${pluralize_default()('updated dependency', updatedDependencies.length, true)}`); core.info(`outputs.dependency-names: ${dependencyNames}`); core.info(`outputs.dependency-type: ${dependencyType}`); @@ -13463,6 +13515,11 @@ function set(updatedDependencies) { core.info(`outputs.directory: ${directory}`); core.info(`outputs.package-ecosystem: ${ecosystem}`); core.info(`outputs.target-branch: ${target}`); + core.info(`outputs.previous-version: ${prevVersion}`); + core.info(`outputs.new-version: ${newVersion}`); + core.info(`outputs.alert-state: ${alertState}`); + core.info(`outputs.ghsa-id: ${ghsaId}`); + core.info(`outputs.cvss: ${cvss}`); core.endGroup(); core.setOutput('updated-dependencies-json', updatedDependencies); core.setOutput('dependency-names', dependencyNames); @@ -13471,6 +13528,11 @@ function set(updatedDependencies) { core.setOutput('directory', directory); core.setOutput('package-ecosystem', ecosystem); core.setOutput('target-branch', target); + core.setOutput('previous-version', prevVersion); + core.setOutput('new-version', newVersion); + core.setOutput('alert-state', alertState); + core.setOutput('ghsa-id', ghsaId); + core.setOutput('cvss', cvss); } function maxDependencyTypes(updatedDependencies) { const dependencyTypes = updatedDependencies.reduce(function (dependencyTypes, dependency) { @@ -13531,10 +13593,11 @@ function run() { // Validate the job const commitMessage = yield getMessage(githubClient, github.context); const branchNames = getBranchNames(github.context); + const alertLookup = (name, version, directory) => getAlert(name, version, directory, githubClient, github.context); if (commitMessage) { // Parse metadata core.info('Parsing Dependabot metadata'); - const updatedDependencies = parse(commitMessage, branchNames.headName, branchNames.baseName); + const updatedDependencies = yield parse(commitMessage, branchNames.headName, branchNames.baseName, alertLookup); if (updatedDependencies.length > 0) { set(updatedDependencies); } diff --git a/src/dependabot/output.test.ts b/src/dependabot/output.test.ts index 83e5ba2..6608d9d 100644 --- a/src/dependabot/output.test.ts +++ b/src/dependabot/output.test.ts @@ -9,6 +9,20 @@ beforeEach(() => { jest.spyOn(core, 'startGroup').mockImplementation(jest.fn()) }) +const baseDependency = { + dependencyName: '', + dependencyType: '', + updateType: '', + directory: '', + packageEcosystem: '', + targetBranch: '', + prevVersion: '', + newVersion: '', + alertState: '', + ghsaId: '', + cvss: 0 +} + test('when given a single dependency it sets its values', async () => { const updatedDependencies = [ { @@ -17,7 +31,12 @@ test('when given a single dependency it sets its values', async () => { updateType: 'version-update:semver-minor', directory: 'wwwroot', packageEcosystem: 'nuget', - targetBranch: 'main' + targetBranch: 'main', + prevVersion: '1.0.2', + newVersion: '1.1.3-beta', + alertState: 'FIXED', + ghsaId: 'VERY_LONG_ID', + cvss: 4.6 } ] @@ -40,36 +59,28 @@ test('when given a single dependency it sets its values', async () => { test('when given a multiple dependencies, it uses the highest values for types', async () => { const updatedDependencies = [ { + ...baseDependency, dependencyName: 'rspec', dependencyType: 'direct:development', - updateType: 'version-update:semver-minor', - directory: '', - packageEcosystem: '', - targetBranch: '' + updateType: 'version-update:semver-minor' }, { + ...baseDependency, dependencyName: 'coffee-rails', dependencyType: 'indirect', - updateType: 'version-update:semver-minor', - directory: '', - packageEcosystem: '', - targetBranch: '' + updateType: 'version-update:semver-minor' }, { + ...baseDependency, dependencyName: 'coffeescript', dependencyType: 'indirect', - updateType: 'version-update:semver-major', - directory: '', - packageEcosystem: '', - targetBranch: '' + updateType: 'version-update:semver-major' }, { + ...baseDependency, dependencyName: 'rspec-coffeescript', dependencyType: 'indirect', - updateType: 'version-update:semver-patch', - directory: '', - packageEcosystem: '', - targetBranch: '' + updateType: 'version-update:semver-patch' } ] @@ -88,12 +99,9 @@ test('when given a multiple dependencies, it uses the highest values for types', test('when the dependency has no update type', async () => { const updatedDependencies = [ { + ...baseDependency, dependencyName: 'coffee-rails', - dependencyType: 'direct:production', - updateType: '', - directory: '', - packageEcosystem: '', - targetBranch: '' + dependencyType: 'direct:production' } ] @@ -116,36 +124,26 @@ test('when the dependency has no update type', async () => { test('when given a multiple dependencies, and some do not have update types', async () => { const updatedDependencies = [ { + ...baseDependency, dependencyName: 'rspec', - dependencyType: 'direct:development', - updateType: '', - directory: '', - packageEcosystem: '', - targetBranch: '' + dependencyType: 'direct:development' }, { + ...baseDependency, dependencyName: 'coffee-rails', dependencyType: 'indirect', - updateType: 'version-update:semver-minor', - directory: '', - packageEcosystem: '', - targetBranch: '' + updateType: 'version-update:semver-minor' }, { + ...baseDependency, dependencyName: 'coffeescript', - dependencyType: 'indirect', - updateType: '', - directory: '', - packageEcosystem: '', - targetBranch: '' + dependencyType: 'indirect' }, { + ...baseDependency, dependencyName: 'rspec-coffeescript', dependencyType: 'indirect', - updateType: 'version-update:semver-patch', - directory: '', - packageEcosystem: '', - targetBranch: '' + updateType: 'version-update:semver-patch' } ] diff --git a/src/dependabot/output.ts b/src/dependabot/output.ts index 6f851d0..8dd5111 100644 --- a/src/dependabot/output.ts +++ b/src/dependabot/output.ts @@ -24,6 +24,11 @@ export function set (updatedDependencies: Array): void { const directory = firstDependency?.directory const ecosystem = firstDependency?.packageEcosystem const target = firstDependency?.targetBranch + const prevVersion = firstDependency?.prevVersion + const newVersion = firstDependency?.newVersion + const alertState = firstDependency?.alertState + const ghsaId = firstDependency?.ghsaId + const cvss = firstDependency?.cvss core.startGroup(`Outputting metadata for ${Pluralize('updated dependency', updatedDependencies.length, true)}`) core.info(`outputs.dependency-names: ${dependencyNames}`) @@ -32,6 +37,11 @@ export function set (updatedDependencies: Array): void { core.info(`outputs.directory: ${directory}`) core.info(`outputs.package-ecosystem: ${ecosystem}`) core.info(`outputs.target-branch: ${target}`) + core.info(`outputs.previous-version: ${prevVersion}`) + core.info(`outputs.new-version: ${newVersion}`) + core.info(`outputs.alert-state: ${alertState}`) + core.info(`outputs.ghsa-id: ${ghsaId}`) + core.info(`outputs.cvss: ${cvss}`) core.endGroup() core.setOutput('updated-dependencies-json', updatedDependencies) @@ -41,6 +51,11 @@ export function set (updatedDependencies: Array): void { core.setOutput('directory', directory) core.setOutput('package-ecosystem', ecosystem) core.setOutput('target-branch', target) + core.setOutput('previous-version', prevVersion) + core.setOutput('new-version', newVersion) + core.setOutput('alert-state', alertState) + core.setOutput('ghsa-id', ghsaId) + core.setOutput('cvss', cvss) } function maxDependencyTypes (updatedDependencies: Array): string { diff --git a/src/dependabot/update_metadata.test.ts b/src/dependabot/update_metadata.test.ts index 844ed2a..35b7aa7 100644 --- a/src/dependabot/update_metadata.test.ts +++ b/src/dependabot/update_metadata.test.ts @@ -1,22 +1,26 @@ import * as updateMetadata from './update_metadata' test('it returns an empty array for a blank string', async () => { - expect(updateMetadata.parse('', 'dependabot/nuget/feature1', 'main')).toEqual([]) + const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 }) + expect(updateMetadata.parse('', 'dependabot/nuget/feature1', 'main', getAlert)).resolves.toEqual([]) }) test('it returns an empty array for commit message with no dependabot yaml fragment', async () => { - const commitMessage = `Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2. + const commitMessage = `Bump coffee-rails from 4.0.1 to 4.2.2 + Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2. - [Release notes](https://github.com/rails/coffee-rails/releases) - [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md) - [Commits](rails/coffee-rails@v4.0.1...v4.2.2) Signed-off-by: dependabot[bot] ` - expect(updateMetadata.parse(commitMessage, 'dependabot/nuget/feature1', 'main')).toEqual([]) + const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 }) + expect(updateMetadata.parse(commitMessage, 'dependabot/nuget/feature1', 'main', getAlert)).resolves.toEqual([]) }) test('it returns the updated dependency information when there is a yaml fragment', async () => { const commitMessage = + 'Bump coffee-rails from 4.0.1 to 4.2.2\n' + 'Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2.\n' + '- [Release notes](https://github.com/rails/coffee-rails/releases)\n' + '- [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md)\n' + @@ -31,7 +35,8 @@ test('it returns the updated dependency information when there is a yaml fragmen '\n' + 'Signed-off-by: dependabot[bot] ' - const updatedDependencies = updateMetadata.parse(commitMessage, 'dependabot/nuget/feature1', 'main') + const getAlert = async () => Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 }) + const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot/nuget/feature1', 'main', getAlert) expect(updatedDependencies).toHaveLength(1) @@ -41,10 +46,16 @@ test('it returns the updated dependency information when there is a yaml fragmen expect(updatedDependencies[0].directory).toEqual('/') expect(updatedDependencies[0].packageEcosystem).toEqual('nuget') expect(updatedDependencies[0].targetBranch).toEqual('main') + expect(updatedDependencies[0].prevVersion).toEqual('4.0.1') + expect(updatedDependencies[0].newVersion).toEqual('4.2.2') + expect(updatedDependencies[0].alertState).toEqual('DISMISSED') + expect(updatedDependencies[0].ghsaId).toEqual('GHSA-III-BBB') + expect(updatedDependencies[0].cvss).toEqual(4.6) }) test('it supports multiple dependencies within a single fragment', async () => { const commitMessage = + 'Bump coffee-rails from 4.0.1 to 4.2.2 in /api/main\n' + 'Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2.\n' + '- [Release notes](https://github.com/rails/coffee-rails/releases)\n' + '- [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md)\n' + @@ -62,28 +73,45 @@ test('it supports multiple dependencies within a single fragment', async () => { '\n' + 'Signed-off-by: dependabot[bot] ' - const updatedDependencies = updateMetadata.parse(commitMessage, 'dependabot/nuget/api/main/feature1', 'main') + const getAlert = async (name: string) => { + if (name === 'coffee-rails') { + return Promise.resolve({ alertState: 'DISMISSED', ghsaId: 'GHSA-III-BBB', cvss: 4.6 }) + } + + return Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 }) + } + + const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot/nuget/api/main/feature1', 'main', getAlert) expect(updatedDependencies).toHaveLength(2) expect(updatedDependencies[0].dependencyName).toEqual('coffee-rails') expect(updatedDependencies[0].dependencyType).toEqual('direct:production') expect(updatedDependencies[0].updateType).toEqual('version-update:semver-minor') - expect(updatedDependencies[0].directory).toEqual('api/main') + expect(updatedDependencies[0].directory).toEqual('/api/main') expect(updatedDependencies[0].packageEcosystem).toEqual('nuget') expect(updatedDependencies[0].targetBranch).toEqual('main') + expect(updatedDependencies[0].prevVersion).toEqual('4.0.1') + expect(updatedDependencies[0].newVersion).toEqual('4.2.2') + expect(updatedDependencies[0].alertState).toEqual('DISMISSED') + expect(updatedDependencies[0].ghsaId).toEqual('GHSA-III-BBB') + expect(updatedDependencies[0].cvss).toEqual(4.6) expect(updatedDependencies[1].dependencyName).toEqual('coffeescript') expect(updatedDependencies[1].dependencyType).toEqual('indirect') expect(updatedDependencies[1].updateType).toEqual('version-update:semver-patch') - expect(updatedDependencies[1].directory).toEqual('api/main') + expect(updatedDependencies[1].directory).toEqual('/api/main') expect(updatedDependencies[1].packageEcosystem).toEqual('nuget') expect(updatedDependencies[1].targetBranch).toEqual('main') + expect(updatedDependencies[1].prevVersion).toEqual('') + expect(updatedDependencies[1].newVersion).toEqual('') + expect(updatedDependencies[1].alertState).toEqual('') + expect(updatedDependencies[1].ghsaId).toEqual('') + expect(updatedDependencies[1].cvss).toEqual(0) }) test('it only returns information within the first fragment if there are multiple yaml documents', async () => { const commitMessage = - 'Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2.\n' + '- [Release notes](https://github.com/rails/coffee-rails/releases)\n' + '- [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md)\n' + '- [Commits](rails/coffee-rails@v4.0.1...v4.2.2)\n' + @@ -104,14 +132,20 @@ test('it only returns information within the first fragment if there are multipl '\n' + 'Signed-off-by: dependabot[bot] ' - const updatedDependencies = updateMetadata.parse(commitMessage, 'dependabot|nuget|api|feature1', 'main') + const getAlert = async () => Promise.resolve({ alertState: '', ghsaId: '', cvss: 0 }) + const updatedDependencies = await updateMetadata.parse(commitMessage, 'dependabot|nuget|feature1', 'main', getAlert) expect(updatedDependencies).toHaveLength(1) expect(updatedDependencies[0].dependencyName).toEqual('coffee-rails') expect(updatedDependencies[0].dependencyType).toEqual('direct:production') expect(updatedDependencies[0].updateType).toEqual('version-update:semver-minor') - expect(updatedDependencies[0].directory).toEqual('api') + expect(updatedDependencies[0].directory).toEqual('/') expect(updatedDependencies[0].packageEcosystem).toEqual('nuget') expect(updatedDependencies[0].targetBranch).toEqual('main') + expect(updatedDependencies[0].prevVersion).toEqual('') + expect(updatedDependencies[0].newVersion).toEqual('') + expect(updatedDependencies[0].alertState).toEqual('') + expect(updatedDependencies[0].ghsaId).toEqual('') + expect(updatedDependencies[0].cvss).toEqual(0) }) diff --git a/src/dependabot/update_metadata.ts b/src/dependabot/update_metadata.ts index b31318d..a5c8f7c 100644 --- a/src/dependabot/update_metadata.ts +++ b/src/dependabot/update_metadata.ts @@ -23,7 +23,7 @@ export interface alertLookup { export async function parse (commitMessage: string, branchName: string, mainBranch: string, lookup: alertLookup): Promise> { const firstLine = commitMessage.split('\n')[0] - const directory = firstLine.match(/ in (?\/[^ ]*)$/) + const directory = firstLine.match(/ in (?[^ ]+)$/) const bumpFragment = commitMessage.match(/^Bumps .* from (?\d[^ ]*) to (?\d[^ ]*)\.$/m) const yamlFragment = commitMessage.match(/^-{3}\n(?[\S|\s]*?)\n^\.{3}\n/m) @@ -33,21 +33,21 @@ export async function parse (commitMessage: string, branchName: string, mainBran // Since we are on the `dependabot` branch (9 letters), the 10th letter in the branch name is the delimiter const delim = branchName[10] const chunks = branchName.split(delim) - const dirname = directory?.groups?.directory ?? "/" - const prev = bumpFragment?.groups?.from ?? "" - const next = bumpFragment?.groups?.to ?? "" + const dirname = directory?.groups?.directory ?? '/' + const prev = bumpFragment?.groups?.from ?? '' + const next = bumpFragment?.groups?.to ?? '' if (data['updated-dependencies']) { - return await Promise.all(data['updated-dependencies'].map(async (dependency) => ({ + return await Promise.all(data['updated-dependencies'].map(async (dependency, index) => ({ dependencyName: dependency['dependency-name'], dependencyType: dependency['dependency-type'], updateType: dependency['update-type'], directory: dirname, packageEcosystem: chunks[1], targetBranch: mainBranch, - prevVersion: prev, - newVersion: next, - ...await lookup(dependency['dependency-name'], prev, dirname) + prevVersion: index === 0 ? prev : "", + newVersion: index === 0 ? next : "", + ...await lookup(dependency['dependency-name'], index === 0 ? prev : "", dirname) }))) } } diff --git a/src/dependabot/verified_commits.ts b/src/dependabot/verified_commits.ts index f11ddd8..885ed26 100644 --- a/src/dependabot/verified_commits.ts +++ b/src/dependabot/verified_commits.ts @@ -86,17 +86,17 @@ export async function getAlert (name: string, version: string, directory: string }`) const nodes = alerts?.repository?.vulnerabilityAlerts?.nodes - const found = nodes.find(a => a.vulnerableRequirements == `= ${version}` - && trimSlashes(a.vulnerableManifestPath) == `${trimSlashes(directory)}/${a.vulnerableManifestFilename}` - && a.securityVulnerability.package.name == name) + const found = nodes.find(a => (version === '' || a.vulnerableRequirements === `= ${version}`) && + trimSlashes(a.vulnerableManifestPath) === `${trimSlashes(directory)}/${a.vulnerableManifestFilename}` && + a.securityVulnerability.package.name === name) - return { - alertState: found?.state ?? "", - ghsaId: found?.securityAdvisory.ghsaId ?? "", + return { + alertState: found?.state ?? '', + ghsaId: found?.securityAdvisory.ghsaId ?? '', cvss: found?.securityAdvisory.cvss.score ?? 0.0 } } -export function trimSlashes(value: string): string { +export function trimSlashes (value: string): string { return value.replace(/^\//, '').replace(/\/$/, '') } diff --git a/src/main.test.ts b/src/main.test.ts index eb9c4c4..df73e1f 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -23,6 +23,7 @@ test('it early exits with an error if github-token is not set', async () => { ) /* eslint-disable no-unused-expressions */ expect(dependabotCommits.getMessage).not.toHaveBeenCalled + expect(dependabotCommits.getAlert).not.toHaveBeenCalled /* eslint-enable no-unused-expressions */ }) @@ -38,6 +39,7 @@ test('it does nothing if the PR is not verified as from Dependabot', async () => expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('PR is not from Dependabot, nothing to do.') ) + expect(dependabotCommits.getAlert).not.toHaveBeenCalled }) test('it does nothing if there is no metadata in the commit', async () => { @@ -52,6 +54,7 @@ test('it does nothing if there is no metadata in the commit', async () => { expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('PR does not contain metadata, nothing to do.') ) + expect(dependabotCommits.getAlert).not.toHaveBeenCalled }) test('it sets the updated dependency as an output for subsequent actions', async () => { @@ -69,12 +72,16 @@ test('it sets the updated dependency as an output for subsequent actions', async '...\n' + '\n' + 'Signed-off-by: dependabot[bot] ' + const mockAlert = { alertState: "FIXED", ghsaId: "GSHA", cvss: 3.4 } jest.spyOn(core, 'getInput').mockReturnValue('mock-token') jest.spyOn(util, 'getBranchNames').mockReturnValue({ headName: 'dependabot|nuget|feature1', baseName: 'main' }) jest.spyOn(dependabotCommits, 'getMessage').mockImplementation(jest.fn( () => Promise.resolve(mockCommitMessage) )) + jest.spyOn(dependabotCommits, 'getAlert').mockImplementation(jest.fn( + () => Promise.resolve(mockAlert) + )) jest.spyOn(core, 'setOutput').mockImplementation(jest.fn()) await run() @@ -92,7 +99,12 @@ test('it sets the updated dependency as an output for subsequent actions', async updateType: 'version-update:semver-minor', directory: '/', packageEcosystem: 'nuget', - targetBranch: 'main' + targetBranch: 'main', + prevVersion: '4.0.1', + newVersion: '4.2.2', + alertState: 'FIXED', + ghsaId: 'GSHA', + cvss: 3.4 } ] ) @@ -107,6 +119,7 @@ test('it sets the updated dependency as an output for subsequent actions', async test('if there are multiple dependencies, it summarizes them', async () => { const mockCommitMessage = + 'Bump coffee-rails from 4.0.1 to 4.2.2 in api/main\n' + 'Bumps [coffee-rails](https://github.com/rails/coffee-rails) from 4.0.1 to 4.2.2.\n' + '- [Release notes](https://github.com/rails/coffee-rails/releases)\n' + '- [Changelog](https://github.com/rails/coffee-rails/blob/master/CHANGELOG.md)\n' + @@ -123,12 +136,16 @@ test('if there are multiple dependencies, it summarizes them', async () => { '...\n' + '\n' + 'Signed-off-by: dependabot[bot] ' + const mockAlert = { alertState: "", ghsaId: "", cvss: 0 } jest.spyOn(core, 'getInput').mockReturnValue('mock-token') jest.spyOn(util, 'getBranchNames').mockReturnValue({ headName: 'dependabot/npm_and_yarn/api/main/feature1', baseName: 'trunk' }) jest.spyOn(dependabotCommits, 'getMessage').mockImplementation(jest.fn( () => Promise.resolve(mockCommitMessage) )) + jest.spyOn(dependabotCommits, 'getAlert').mockImplementation(jest.fn( + () => Promise.resolve(mockAlert) + )) jest.spyOn(core, 'setOutput').mockImplementation(jest.fn()) await run() @@ -146,7 +163,12 @@ test('if there are multiple dependencies, it summarizes them', async () => { updateType: 'version-update:semver-minor', directory: 'api/main', packageEcosystem: 'npm_and_yarn', - targetBranch: 'trunk' + targetBranch: 'trunk', + prevVersion: '4.0.1', + newVersion: '4.2.2', + alertState: '', + ghsaId: '', + cvss: 0 }, { dependencyName: 'coffeescript', @@ -154,7 +176,12 @@ test('if there are multiple dependencies, it summarizes them', async () => { updateType: 'version-update:semver-major', directory: 'api/main', packageEcosystem: 'npm_and_yarn', - targetBranch: 'trunk' + targetBranch: 'trunk', + prevVersion: '', + newVersion: '', + alertState: '', + ghsaId: '', + cvss: 0 } ] ) @@ -176,6 +203,7 @@ test('it sets the action to failed if there is an unexpected exception', async ( await run() + expect(dependabotCommits.getAlert).not.toHaveBeenCalled expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('Something bad happened!') ) @@ -199,6 +227,7 @@ test('it sets the action to failed if there is a request error', async () => { await run() + expect(dependabotCommits.getAlert).not.toHaveBeenCalled expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('(500) Something bad happened!') )