Skip to content

Conversation

@aegilops
Copy link
Contributor

@aegilops aegilops commented Dec 4, 2025

Adds branch scanning, dependency submission, and more robustness.

  • fetching SBOM diffs for non-default branches
  • automatic dependency snapshot submission using Component Detection
  • better CLI options and documentation
  • improved advisory sync reliability
  • support for org listing with Enterprise Server

Copilot AI review requested due to automatic review settings December 4, 2025 17:54
Copilot AI review requested due to automatic review settings December 8, 2025 17:52
Copy link

Copilot AI left a 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>
Copilot AI review requested due to automatic review settings December 8, 2025 18:07
Copy link

Copilot AI left a 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.

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));
Copy link

Copilot AI Dec 8, 2025

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:

  1. Extracting this as a named constant (e.g., SNAPSHOT_INGESTION_DELAY_MS) to make its purpose clear
  2. Making it configurable through options if different GitHub instances have different ingestion times
  3. Adding a comment explaining why this specific duration was chosen

The same applies to the 3000ms delay on line 539.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

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>
Copilot AI review requested due to automatic review settings December 8, 2025 18:16
aegilops and others added 2 commits December 8, 2025 18:18
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI commented Dec 8, 2025

@aegilops I've opened a new pull request, #28, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link

Copilot AI left a 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.

Copilot AI and others added 2 commits December 8, 2025 18:29
Co-authored-by: aegilops <41705651+aegilops@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 8, 2025 18:30
Co-authored-by: aegilops <41705651+aegilops@users.noreply.github.com>
Copy link

Copilot AI left a 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
Copy link

Copilot AI Dec 8, 2025

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.

Suggested change
## [2025-12-04] – 0.2.0 - Branch scanning and dependency submission
## [2025-12-08] – 0.2.0 - Branch scanning and dependency submission

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +169
// 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 : ''}`;
}

Copy link

Copilot AI Dec 8, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +168
// 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 : ''}`;
}
Copy link

Copilot AI Dec 8, 2025

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.

Suggested change
// 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
}));

Copilot uses AI. Check for mistakes.
Comment on lines +594 to +635
// 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); }
}
}
}
}
Copy link

Copilot AI Dec 8, 2025

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.

Copilot uses AI. Check for mistakes.
Copilot AI and others added 3 commits December 8, 2025 18:43
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>
Copilot AI review requested due to automatic review settings December 9, 2025 09:20
Copy link

Copilot AI left a 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]];
Copy link

Copilot AI Dec 9, 2025

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.

Suggested change
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."); })();

Copilot uses AI. Check for mistakes.
totalRepos = 1;
const [org, repoName] = this.opts.repo.split("/");
orgRepoMap[org] = [await this.getRepo(org, repoName)];
this.summary.orgs = orgs;
Copy link

Copilot AI Dec 9, 2025

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.

Suggested change
this.summary.orgs = orgs;

Copilot uses AI. Check for mistakes.
Comment on lines +601 to +642
// 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); }
}
}
}
}
Copy link

Copilot AI Dec 9, 2025

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.

Copilot uses AI. Check for mistakes.
console.debug((e as Error).message);
}

if (sbom.error) this.summary.failedCount++; else this.summary.successCount++;
Copy link

Copilot AI Dec 9, 2025

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++;

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +101
if (debug) {
console.debug(chalk.blue("Debug logging enabled"));
} else {
console.debug = () => { };
}

Copy link

Copilot AI Dec 9, 2025

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.

Suggested change
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");

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants