-
Notifications
You must be signed in to change notification settings - Fork 3
Add Dependency Review and Submission #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…ew and submission
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 18 changed files in this pull request and generated 15 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ity/github-sbom-toolkit into dependency-review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 18 changed files in this pull request and generated 12 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/sbomCollector.ts
Outdated
| console.debug(chalk.blue(`Force-submission enabled: submitting component snapshot for ${fullName} branch ${b.name}...`)); | ||
| if (await submitSnapshotIfPossible({ octokit: this.octokit, owner: org, repo: repo.name, branch: b.name, languages: this.opts.submitLanguages, quiet: this.opts.quiet, componentDetectionBinPath: this.opts.componentDetectionBinPath })) { | ||
| // brief delay to allow snapshot ingestion | ||
| await new Promise(r => setTimeout(r, 1500)); |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The magic number 1500 (milliseconds) for the delay after snapshot submission is hardcoded. This delay allows time for snapshot ingestion before retrying the dependency review diff. Consider:
- Extracting this as a named constant (e.g.,
SNAPSHOT_INGESTION_DELAY_MS) to make its purpose clear - Making it configurable through options if different GitHub instances have different ingestion times
- Adding a comment explaining why this specific duration was chosen
The same applies to the 3000ms delay on line 539.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot open a new pull request to apply changes based on this feedback
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 18 changed files in this pull request and generated 12 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: aegilops <41705651+aegilops@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: aegilops <41705651+aegilops@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 18 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,32 @@ | |||
| # Changelog | |||
|
|
|||
| ## [2025-12-04] – 0.2.0 - Branch scanning and dependency submission | |||
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The CHANGELOG.md file shows a date of [2025-12-04] on line 3, but the current date is December 8, 2025. This appears to be a date in the past (4 days ago). While this could be intentional if the work was completed then, typically changelogs reflect the actual release date. Verify this is the intended date.
| ## [2025-12-04] – 0.2.0 - Branch scanning and dependency submission | |
| ## [2025-12-08] – 0.2.0 - Branch scanning and dependency submission |
| // Some ecosystems require a namespace (e.g., Maven, NuGet) | ||
| const ecosystemsWithNamespace = new Set(['maven', 'nuget', 'composer', 'golang']); | ||
| if (ecosystemsWithNamespace.has(change.ecosystem) && change.namespace) { | ||
| p = `pkg:${change.ecosystem}/${change.namespace}/${change.name}${change.version ? '@' + change.version : ''}`; | ||
| } else { | ||
| p = `pkg:${change.ecosystem}/${change.name}${change.version ? '@' + change.version : ''}`; | ||
| } | ||
|
|
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the malwareMatcher.ts file, lines 162-169 appear to be orphaned code that doesn't belong to any function. These lines seem to be constructing a purl for Maven/NuGet ecosystems with namespaces, but they are placed outside of any function context. This code should either be removed or properly integrated into the enumerateDiffPackages function where it logically belongs.
| // Some ecosystems require a namespace (e.g., Maven, NuGet) | ||
| const ecosystemsWithNamespace = new Set(['maven', 'nuget', 'composer', 'golang']); | ||
| if (ecosystemsWithNamespace.has(change.ecosystem) && change.namespace) { | ||
| p = `pkg:${change.ecosystem}/${change.namespace}/${change.name}${change.version ? '@' + change.version : ''}`; | ||
| } else { | ||
| p = `pkg:${change.ecosystem}/${change.name}${change.version ? '@' + change.version : ''}`; | ||
| } |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The enumerateBasePackages function is annotated to return packages with __branch property (line 150), but the code at line 161 doesn't actually set this property on any returned packages. The comment at line 161 mentions "Annotate with default branch for reporting (if known)" but no annotation code follows. Either the code to set __branch is missing, or the return type annotation should not include __branch for base packages.
| // Some ecosystems require a namespace (e.g., Maven, NuGet) | |
| const ecosystemsWithNamespace = new Set(['maven', 'nuget', 'composer', 'golang']); | |
| if (ecosystemsWithNamespace.has(change.ecosystem) && change.namespace) { | |
| p = `pkg:${change.ecosystem}/${change.namespace}/${change.name}${change.version ? '@' + change.version : ''}`; | |
| } else { | |
| p = `pkg:${change.ecosystem}/${change.name}${change.version ? '@' + change.version : ''}`; | |
| } | |
| const branch = (repo as any).defaultBranch || undefined; | |
| return list.map(pkg => ({ | |
| ...pkg, | |
| __branch: branch | |
| })); |
| // Move applyQueries to module scope | ||
| function applyQueries( | ||
| candidatePurls: string[], | ||
| queries: ParsedQuery[], | ||
| found: Map<string, string>, | ||
| branchTag?: string, | ||
| fallbackVersion?: string | ||
| ) { | ||
| const unique = Array.from(new Set(candidatePurls)); | ||
| for (const p of unique) { | ||
| const pLower = p.toLowerCase(); | ||
| const outKey = branchTag ? `${p}@${branchTag}` : p; | ||
| for (const q of queries) { | ||
| if (q.isPrefixWildcard) { | ||
| const prefix = q.lower.slice(0, -1); | ||
| if (pLower.startsWith(prefix)) { if (!found.has(outKey)) found.set(outKey, q.raw); } | ||
| continue; | ||
| } | ||
| if (q.versionConstraint && q.type && q.name) { | ||
| if (!pLower.startsWith("pkg:")) continue; | ||
| const body = p.slice(4); | ||
| const atIdx = body.indexOf("@"); | ||
| const main = atIdx >= 0 ? body.slice(0, atIdx) : body; | ||
| const ver = atIdx >= 0 ? body.slice(atIdx + 1) : fallbackVersion; | ||
| const slashIdx = main.indexOf("/"); | ||
| if (slashIdx < 0) continue; | ||
| const pType = main.slice(0, slashIdx).toLowerCase(); | ||
| const pName = main.slice(slashIdx + 1); | ||
| if (pType === q.type && pName.toLowerCase() === q.name.toLowerCase() && ver) { | ||
| try { | ||
| const coerced = semver.coerce(ver)?.version || ver; | ||
| if (semver.valid(coerced) && semver.satisfies(coerced, q.versionConstraint, { includePrerelease: true })) { | ||
| if (!found.has(outKey)) found.set(outKey, q.raw); | ||
| } | ||
| } catch { /* ignore */ } | ||
| } | ||
| } else if (q.exact) { | ||
| if (pLower === q.exact) { if (!found.has(outKey)) found.set(outKey, q.raw); } | ||
| } | ||
| } | ||
| } | ||
| } |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The applyQueries function at line 595-635 in sbomCollector.ts is declared at module scope (outside the class), which breaks the expected encapsulation. This function appears to be extracted from inside the searchByPurlsWithReasons method but is placed incorrectly. The comment at line 594 says "// Move applyQueries to module scope" which suggests this was intentional, but it's placed in the middle of the class definition rather than before or after it, making the code structure confusing and potentially breaking the class context.
Co-authored-by: aegilops <41705651+aegilops@users.noreply.github.com>
…fa4-4def-4364-b631-731c97106a16 Make snapshot ingestion delays configurable
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 15 out of 18 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| const orgs = this.opts.org ? [this.opts.org] : await this.listEnterpriseOrgs(this.opts.enterprise!); | ||
| const orgs = this.opts.org ? [this.opts.org] : this.opts.enterprise ? await this.listEnterpriseOrgs(this.opts.enterprise, this.opts.ghes) : [this.opts.repo.split("/")[0]]; |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At line 168, when this.opts.repo is specified, the code attempts to access this.opts.repo.split("/")[0] without first checking if this.opts.repo is defined. While the constructor validates the repo format (lines 54-61), TypeScript may still treat this.opts.repo as potentially undefined here. The code should handle the case where repo is undefined more explicitly, or the ternary expression should be restructured to avoid this potential runtime error.
| const orgs = this.opts.org ? [this.opts.org] : this.opts.enterprise ? await this.listEnterpriseOrgs(this.opts.enterprise, this.opts.ghes) : [this.opts.repo.split("/")[0]]; | |
| const orgs = this.opts.org | |
| ? [this.opts.org] | |
| : this.opts.enterprise | |
| ? await this.listEnterpriseOrgs(this.opts.enterprise, this.opts.ghes) | |
| : this.opts.repo | |
| ? [this.opts.repo.split("/")[0]] | |
| : (() => { throw new Error("No org, enterprise, or repo specified in options."); })(); |
| totalRepos = 1; | ||
| const [org, repoName] = this.opts.repo.split("/"); | ||
| orgRepoMap[org] = [await this.getRepo(org, repoName)]; | ||
| this.summary.orgs = orgs; |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 187 sets this.summary.orgs = orgs redundantly since orgs was already assigned to this.summary.orgs at line 169. This duplicate assignment serves no purpose and should be removed.
| this.summary.orgs = orgs; |
| // Move applyQueries to module scope | ||
| function applyQueries( | ||
| candidatePurls: string[], | ||
| queries: ParsedQuery[], | ||
| found: Map<string, string>, | ||
| branchTag?: string, | ||
| fallbackVersion?: string | ||
| ) { | ||
| const unique = Array.from(new Set(candidatePurls)); | ||
| for (const p of unique) { | ||
| const pLower = p.toLowerCase(); | ||
| const outKey = branchTag ? `${p}@${branchTag}` : p; | ||
| for (const q of queries) { | ||
| if (q.isPrefixWildcard) { | ||
| const prefix = q.lower.slice(0, -1); | ||
| if (pLower.startsWith(prefix)) { if (!found.has(outKey)) found.set(outKey, q.raw); } | ||
| continue; | ||
| } | ||
| if (q.versionConstraint && q.type && q.name) { | ||
| if (!pLower.startsWith("pkg:")) continue; | ||
| const body = p.slice(4); | ||
| const atIdx = body.indexOf("@"); | ||
| const main = atIdx >= 0 ? body.slice(0, atIdx) : body; | ||
| const ver = atIdx >= 0 ? body.slice(atIdx + 1) : fallbackVersion; | ||
| const slashIdx = main.indexOf("/"); | ||
| if (slashIdx < 0) continue; | ||
| const pType = main.slice(0, slashIdx).toLowerCase(); | ||
| const pName = main.slice(slashIdx + 1); | ||
| if (pType === q.type && pName.toLowerCase() === q.name.toLowerCase() && ver) { | ||
| try { | ||
| const coerced = semver.coerce(ver)?.version || ver; | ||
| if (semver.valid(coerced) && semver.satisfies(coerced, q.versionConstraint, { includePrerelease: true })) { | ||
| if (!found.has(outKey)) found.set(outKey, q.raw); | ||
| } | ||
| } catch { /* ignore */ } | ||
| } | ||
| } else if (q.exact) { | ||
| if (pLower === q.exact) { if (!found.has(outKey)) found.set(outKey, q.raw); } | ||
| } | ||
| } | ||
| } | ||
| } |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The applyQueries function is defined inside the searchByPurlsWithReasons method but the comment on line 601 says "Move applyQueries to module scope". This function is currently nested within the method, making it inaccessible to other methods and created anew on each call. The function should be moved to module scope (outside the class) as the comment indicates, both for reusability and performance.
| console.debug((e as Error).message); | ||
| } | ||
|
|
||
| if (sbom.error) this.summary.failedCount++; else this.summary.successCount++; |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The success/fail counting logic at line 344 is only executed when this.opts.includeBranches && sbom && sbom.sbom is true (inside the branch scanning block), but it should be executed for all SBOM fetches. When branch scanning is disabled or conditions aren't met, newly fetched SBOMs won't be counted in the summary statistics. This logic should be moved outside the branch scanning conditional block (after line 350) so it runs for all newly fetched SBOMs.
See below for a potential fix:
// Write freshly fetched SBOM immediately if a cache directory is configured
if (this.opts.loadFromDir && this.opts.syncSboms && this.opts.loadFromDir.length) {
try { writeOne(sbom, { outDir: this.opts.loadFromDir }); } catch { /* ignore write errors */ }
}
}
if (sbom) {
if (sbom.error) this.summary.failedCount++; else this.summary.successCount++;
| if (debug) { | ||
| console.debug(chalk.blue("Debug logging enabled")); | ||
| } else { | ||
| console.debug = () => { }; | ||
| } | ||
|
|
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overriding console.debug globally (line 99) to disable debug logging can cause issues if other libraries or parts of the code rely on console.debug for their own purposes. A better approach would be to use a custom logger function or wrap console.debug in a conditional check throughout the codebase.
| if (debug) { | |
| console.debug(chalk.blue("Debug logging enabled")); | |
| } else { | |
| console.debug = () => { }; | |
| } | |
| // Custom debug logger to avoid overriding console.debug globally | |
| const logDebug = (...args: any[]) => { | |
| if (debug) { | |
| console.debug(chalk.blue(...args)); | |
| } | |
| }; | |
| logDebug("Debug logging enabled"); |
Adds branch scanning, dependency submission, and more robustness.