Skip to content

feat: populate ExtraFields with dependency chains, CVE, fixVersion#296

Merged
andrzej-janczak merged 4 commits into
masterfrom
feat/dependency-chains
Jun 1, 2026
Merged

feat: populate ExtraFields with dependency chains, CVE, fixVersion#296
andrzej-janczak merged 4 commits into
masterfrom
feat/dependency-chains

Conversation

@andrzej-janczak
Copy link
Copy Markdown
Contributor

@andrzej-janczak andrzej-janczak commented May 28, 2026

Summary

  • Bumps codacy-engine-golang-seed to v8.0.4 (which adds ExtraFields json.RawMessage to Issue)
  • For each vulnerability issue, populates ExtraFields with a JSON object containing:
    • dependenciesChains: up to 10 DFS chains from root to vulnerable package (root-first ordering); each chain capped at 20 nodes (tail kept, vulnerable package last); single-element chain = direct dependency
    • CVE: vulnerability ID (same as SourceID)
    • fixVersion: least disruptive fix version, or "" if none available
  • Adds buildDependencyChains + trimChainTail helpers
  • Cycle guard: per-DFS-path visited set prevents infinite loops on malformed dep graphs

Example output

{
  "extraFields": {
    "dependenciesChains": [
      ["root-app", "@codacy/codacy-mcp", "@modelcontextprotocol/sdk"],
      ["root-app", "express-rate-limit", "express", "path-to-regexp"]
    ],
    "CVE": "CVE-2024-12345",
    "fixVersion": "1.2.4"
  }
}

Part of

Dependency chain propagation initiative — TASK 2 of the SRM dependency chains plan.

Test plan

Tested locally ✅

DEMO=/Users/czak/.claude/jobs/e7428284/tmp/demo
docker run --rm \
  -v "$DEMO":/src \
  -v "$DEMO/.codacyrc":/.codacyrc \
  -v "$HOME/Library/Caches/trivy/db":/dist/cache/codacy-trivy/db:ro \
  codacy-trivy:pr296 \
  | jq -c 'select(.extraFields) | {cve:.extraFields.CVE, chains:.extraFields.dependenciesChains}'
{"cve":"CVE-2021-3749","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2025-27152","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-25639","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42033","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42035","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42043","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2020-28168","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2023-45857","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2025-62718","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-40175","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42034","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42036","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42038","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42039","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42041","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42042","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2026-42040","chains":[["npm/axios@0.21.0"]]}
{"cve":"CVE-2022-0536","chains":[["npm/axios@0.21.0","npm/follow-redirects@1.14.7"]]}
{"cve":"CVE-2023-26159","chains":[["npm/axios@0.21.0","npm/follow-redirects@1.14.7"]]}
{"cve":"CVE-2024-28849","chains":[["npm/axios@0.21.0","npm/follow-redirects@1.14.7"]]}
{"cve":"GHSA-r4q5-vmmm-2653","chains":[["npm/axios@0.21.0","npm/follow-redirects@1.14.7"]]}
    ~/GI/codacy/codacy-trivy/.cl/worktrees/pr296-fix    worktree-pr296-fix ? 
  • All internal/tool tests pass (updated TestRun to include expected ExtraFields)
  • Direct dependency → single-element chain
  • Mock runner tests (TestRunInvalidExecutionConfiguration, TestRunNewRunnerError, etc.) unchanged

🤖 Generated with Claude Code

@andrzej-janczak andrzej-janczak requested a review from a team as a code owner May 28, 2026 09:49
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 28, 2026

Up to standards ✅

🟢 Issues 1 high

Results:
1 new issue

Category Results
ErrorProne 1 high (1 false positive)

View in Codacy

🟢 Metrics 19 complexity · 0 duplication

Metric Results
Complexity 19
Duplication 0

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the codacy-engine-golang-seed dependency and introduces dependency chain tracking for identified vulnerabilities, adding this information to the issue's extra fields. The review feedback highlights critical issues in the buildDependencyChains implementation, including traversing dependencies in the wrong direction, a key mismatch when looking up packages, and a potential nil pointer dereference. Suggestions are provided to refactor the DFS traversal by passing the full package list directly and building an inverted parent map.

Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go Outdated
Copy link
Copy Markdown

@codacy-production codacy-production Bot 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

The PR is currently not up to standards due to critical logic errors and potential runtime panics. While it introduces the structure for populating ExtraFields, the implementation of buildDependencyChains traverses the dependency graph in the wrong direction (following children instead of parents), failing the requirement to show paths from the project root to the vulnerable package. Additionally, there is a significant risk of a nil pointer dereference when looking up packages by PURL.

Most of the acceptance criteria regarding chain limits, truncation, and cycle detection are implemented but remain unverified by tests. The cyclomatic complexity of the DFS implementation also exceeds quality thresholds, suggesting a need for refactoring into more modular, testable components.

About this PR

  • The PR lacks specific unit tests for helper functions like buildDependencyChains and trimChainTail. Given the complexity of DFS with cycle detection and truncation, these functions should be tested in isolation with various graph scenarios (e.g., multi-node chains, cycles, and chains exceeding the 20-node limit) before merging.
  • Variable naming in the traversal loop (e.g., using 'parent' for items in pkg.DependsOn) is misleading because the code currently treats child dependencies as parents. This should be corrected alongside the traversal logic refactor.

Test suggestions

  • Direct dependency results in a single-element chain in ExtraFields
  • Transitive dependency chain correctly identifies path from root to vulnerable package
  • Limit the number of returned dependency chains to 10
  • Truncate dependency chains to 20 nodes while retaining the tail (vulnerable package)
  • Prevent infinite recursion when encountering cycles in the dependency graph
  • Correct population of CVE and fixVersion when a fix is available
  • Unit test for buildDependencyChains coverage
  • Unit test for trimChainTail coverage
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Transitive dependency chain correctly identifies path from root to vulnerable package
2. Limit the number of returned dependency chains to 10
3. Truncate dependency chains to 20 nodes while retaining the tail (vulnerable package)
4. Prevent infinite recursion when encountering cycles in the dependency graph
5. Unit test for buildDependencyChains coverage
6. Unit test for trimChainTail coverage

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go Outdated
Copy link
Copy Markdown

@codacy-production codacy-production Bot 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

This PR is currently not up to standards due to a combination of performance bottlenecks and edge-case logic flaws in the new dependency chain implementation.

While the core functionality for populating ExtraFields is present, the implementation performs O(N*M) work by rebuilding dependency maps for every vulnerability found. Additionally, there is a logic gap in the cycle-detection fallback: it can exceed the maxDependencyChains limit (10), which contradicts the acceptance criteria. Finally, the complexity of the internal dfs function has increased since the last review, indicating a need for refactoring into smaller, testable units.

About this PR

  • The dependency graph traversal logic should be optimized. Currently, the maps used to navigate the graph are rebuilt for every single vulnerability. Since the project's dependency structure is static during the scan, these should be precomputed once.

Test suggestions

  • Direct dependency produces a single-element chain
  • Transitive dependency produces root-first chains
  • Multiple paths to root produce multiple chains (up to 10)
  • Chain length capped at 20 nodes, keeping the tail of the chain
  • Cycles in dependency graph are handled gracefully without infinite loops
  • ExtraFields contains expected JSON keys (CVE, fixVersion, dependenciesChains) in tool output
  • Package without PURL falls back to using UID as the node name in the chain

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go Outdated
Comment thread internal/tool/tool.go
andrzej-janczak added a commit that referenced this pull request May 28, 2026
Address PR review comments on #296:
- Precompute pkgByUID and parentsByUID once per result via buildPackageGraph,
  avoiding O(N*M) reconstruction inside the vulnerability loop.
- buildDependencyChains now takes a targetUID + packageGraph; the call site
  uses vuln.PkgIdentifier.UID (with PURL fallback).
- Extract the DFS into a chainBuilder struct with nodeName/tryRecord helpers,
  dropping the closure's cyclomatic complexity below the threshold.
- tryRecord enforces maxDependencyChains for the cycle-fallback branch too,
  preventing one extra chain past the cap.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
andrzej-janczak and others added 3 commits May 29, 2026 11:31
For each vulnerability found by Trivy, emit extraFields containing:
- dependenciesChains: up to 10 DFS paths root-first to the vulnerable
  pkg, each capped at 20 nodes (tail kept); single-element chain = direct dep
- CVE: vulnerability ID
- fixVersion: least disruptive fix version, or empty string

Bumps codacy-engine-golang-seed to v8.0.4 (ExtraFields on Issue).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Three bugs in buildDependencyChains:
1. DependsOn lists children (what a pkg depends on), not parents. Fix:
   build inverted parentsByUID map (childUID -> []parentUIDs) and DFS
   upward to roots.
2. pkgByPurl was keyed by PURL but DependsOn contains UIDs — always
   missed. Fix: key by pkg.Identifier.UID and pass packages slice.
3. Nil dereference: PURL dereferenced before checking ok. Fix: check
   ok before calling purlPrettyPrint.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Covers: direct dep, single transitive chain, deep chain, multiple paths,
target not found, missing PURL fallback to UID, max chains limit (10),
chain length trim (20), and cycle termination.

Also fixes buildDependencyChains: when all parent branches are blocked
by the cycle guard, treat current node as root and emit the chain rather
than silently discarding it.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@andrzej-janczak andrzej-janczak force-pushed the feat/dependency-chains branch from 4c82892 to be14768 Compare May 29, 2026 09:33
Trivy's pkg.DependsOn references Package.ID (name@version), while
Identifier.UID is an unrelated calculated hash. buildDependencyChains
keyed the inverted parent map by UID, so lookups always missed and
every chain collapsed to a single element on real scan data.

- buildPackageGraph: precompute pkgByID + inverted parentsByID once per
  result (keyed by Package.ID), fixing both the keying bug and the
  O(vulns*packages) per-vuln rebuild.
- target the vulnerable package by vuln.PkgID (Trivy's graph key).
- fold the maxDependencyChains cap into a single record helper.
- tests: makePkg now sets Identifier.UID distinct from Package.ID so the
  suite fails if the graph is keyed by UID; add regression test and
  realistic PkgID/SBOM PkgID-property fixtures in TestRun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@andrzej-janczak andrzej-janczak merged commit 73820fe into master Jun 1, 2026
8 checks passed
@andrzej-janczak andrzej-janczak deleted the feat/dependency-chains branch June 1, 2026 07:47
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