* Initial plan * Initial plan for adding patched versions to vulnerability summary Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Add patched version column to vulnerability summary table Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Optimize API calls to use Set and Promise.all for better performance Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Remove type assertions and optimize ecosystem lookups with normalization Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Extract patch version type checking into helper function for clarity Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Support multiple version ranges per package in advisory lookup Handle scenarios where the same package has multiple vulnerable version ranges with different patched versions (e.g., GHSA-gwq6-fmvp-qp68 with .NET packages). - Store all vulnerability entries with version ranges, not just one per ecosystem - Implement version range matching to select correct patch version - Match package by ecosystem, name, AND version range - Add comprehensive test for multi-range scenario Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Fix first_patched_version extraction to handle string format The GitHub Advisory API returns first_patched_version as a simple string (e.g., "112.0.0"), not an object with an identifier field. Updated extractPatchVersionId to handle string format primarily while maintaining backward compatibility with object format. - Fixed extractPatchVersionId to check for string first - Updated test mocks to match actual API response format - Added specific test for RestSharp GHSA-4rr6-2v9v-wcpc case - Verified with actual API responses from multiple advisories Fixes issue where patched versions were showing as "N/A" instead of the actual version number. Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Rebuild dist with patched version fix The previous fix to extractPatchVersionId wasn't deployed because the dist/ folder (compiled JavaScript) wasn't rebuilt. This commit rebuilds and packages the action with npm run build && npm run package to include the fix. Changes in dist/: - Updated extractPatchVersionId to handle string format first - Includes all async vulnerability summary logic with API calls - Properly extracts patched versions from GitHub Advisory API This should resolve the issue where patched versions showed as "N/A" in actual GitHub Actions runs. Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> * Add comprehensive debug logging for patch version lookup Added detailed debug logging to help troubleshoot patch version issues: - Log when fetching advisory data from API - Log number of vulnerability entries found - Log each patch info entry added with details - Log when no patch version is found - Log during lookup phase with package details - Log when patch version is found vs not found - Log available entries when no match is found This will make it much easier to diagnose issues in GitHub Actions debug mode. Co-authored-by: felickz <1760475+felickz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: felickz <1760475+felickz@users.noreply.github.com>
156 lines
3.7 KiB
TypeScript
156 lines
3.7 KiB
TypeScript
/**
|
|
* This scripts creates example markdown files for the summary in the ./tmp folder.
|
|
* You can use it to preview changes to the summary.
|
|
*
|
|
* You can execute it like this:
|
|
* npx ts-node scripts/create_summary.ts
|
|
*/
|
|
|
|
import {Change, Changes, ConfigurationOptions, Scorecard} from '../src/schemas'
|
|
import {createTestChange} from '../__tests__/fixtures/create-test-change'
|
|
import {InvalidLicenseChanges} from '../src/licenses'
|
|
import * as fs from 'fs'
|
|
import * as core from '@actions/core'
|
|
import * as summary from '../src/summary'
|
|
import * as path from 'path'
|
|
|
|
const defaultConfig: ConfigurationOptions = {
|
|
vulnerability_check: true,
|
|
license_check: true,
|
|
fail_on_severity: 'high',
|
|
fail_on_scopes: ['runtime'],
|
|
allow_ghsas: [],
|
|
allow_licenses: ['MIT'],
|
|
deny_licenses: [],
|
|
deny_packages: [],
|
|
deny_groups: [],
|
|
allow_dependencies_licenses: [
|
|
'pkg:npm/express@4.17.1',
|
|
'pkg:pypi/requests',
|
|
'pkg:pypi/certifi',
|
|
'pkg:pypi/pycrypto@2.6.1'
|
|
],
|
|
comment_summary_in_pr: true,
|
|
retry_on_snapshot_warnings: false,
|
|
retry_on_snapshot_warnings_timeout: 120,
|
|
warn_only: false,
|
|
warn_on_openssf_scorecard_level: 3,
|
|
show_openssf_scorecard: true
|
|
}
|
|
|
|
const scorecard: Scorecard = {
|
|
dependencies: [
|
|
{
|
|
change: {
|
|
change_type: 'added',
|
|
manifest: '',
|
|
ecosystem: 'unknown',
|
|
name: 'castore',
|
|
version: '0.1.17',
|
|
package_url: 'pkg:hex/castore@0.1.17',
|
|
license: null,
|
|
source_repository_url: null,
|
|
scope: 'runtime',
|
|
vulnerabilities: []
|
|
},
|
|
scorecard: null
|
|
}
|
|
]
|
|
}
|
|
|
|
const tmpDir = path.resolve(__dirname, '../tmp')
|
|
|
|
const createExampleSummaries = async (): Promise<void> => {
|
|
await fs.promises.mkdir(tmpDir, {recursive: true})
|
|
|
|
await createNonIssueSummary()
|
|
await createFullSummary()
|
|
}
|
|
|
|
const createNonIssueSummary = async (): Promise<void> => {
|
|
await createSummary(
|
|
[],
|
|
{forbidden: [], unresolved: [], unlicensed: []},
|
|
[],
|
|
defaultConfig,
|
|
'non-issue-summary.md'
|
|
)
|
|
}
|
|
|
|
const createFullSummary = async (): Promise<void> => {
|
|
const changes = [createTestChange()]
|
|
const licenses: InvalidLicenseChanges = {
|
|
forbidden: [
|
|
createTestChange({
|
|
name: 'underscore',
|
|
version: '1.12.0',
|
|
license: 'Apache 2.0'
|
|
})
|
|
],
|
|
unresolved: [
|
|
createTestChange({
|
|
name: 'octoinvader',
|
|
license: 'Non SPDX License'
|
|
}),
|
|
createTestChange({
|
|
name: 'owner/action-1',
|
|
license: 'XYZ-License',
|
|
version: 'v1.2.2',
|
|
manifest: '.github/workflows/action.yml'
|
|
})
|
|
],
|
|
unlicensed: [
|
|
createTestChange({
|
|
name: 'my-other-dependency',
|
|
license: null
|
|
}),
|
|
createTestChange({
|
|
name: 'owner/action-2',
|
|
version: 'main',
|
|
license: null,
|
|
manifest: '.github/workflows/action.yml'
|
|
})
|
|
]
|
|
}
|
|
|
|
await createSummary(changes, licenses, [], defaultConfig, 'full-summary.md')
|
|
}
|
|
|
|
async function createSummary(
|
|
vulnerabilities: Changes,
|
|
licenseIssues: InvalidLicenseChanges,
|
|
denied: Change[],
|
|
config: ConfigurationOptions,
|
|
fileName: string
|
|
): Promise<void> {
|
|
summary.addSummaryToSummary(
|
|
vulnerabilities,
|
|
licenseIssues,
|
|
denied,
|
|
scorecard,
|
|
config
|
|
)
|
|
await summary.addChangeVulnerabilitiesToSummary(
|
|
vulnerabilities,
|
|
config.fail_on_severity
|
|
)
|
|
summary.addLicensesToSummary(licenseIssues, defaultConfig)
|
|
|
|
const allChanges = [
|
|
...vulnerabilities,
|
|
...licenseIssues.forbidden,
|
|
...licenseIssues.unresolved,
|
|
...licenseIssues.unlicensed
|
|
]
|
|
|
|
summary.addScannedFiles(allChanges)
|
|
|
|
const text = core.summary.stringify()
|
|
await fs.promises.writeFile(path.resolve(tmpDir, fileName), text, {
|
|
flag: 'w'
|
|
})
|
|
core.summary.emptyBuffer()
|
|
}
|
|
|
|
createExampleSummaries()
|