Skip to content

feat(cli): add remediate command with dry-run and apply modes - #604

Open
ruromero wants to merge 7 commits into
guacsec:mainfrom
ruromero:TC-5414
Open

feat(cli): add remediate command with dry-run and apply modes#604
ruromero wants to merge 7 commits into
guacsec:mainfrom
ruromero:TC-5414

Conversation

@ruromero

@ruromero ruromero commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add remediate CLI command orchestrating the full remediation pipeline: manifest discovery → DA backend scan → remediation extraction → version updates or dry-run preview
  • Support pom.xml (Maven) and libs.versions.toml (Gradle) manifests with directory-mode recursive discovery
  • Include provider/source filtering, bundled/per-dependency report grouping, and CI-friendly exit codes (0/1/2)
  • Make --dry-run and --apply mutually exclusive via yargs conflicts + defense-in-depth guard (TC-5536)
  • Normalize provider list with .trim().filter(Boolean) to handle whitespace in comma-separated input (TC-5537)
  • Handle fixedIn version arrays and prefer trustedContent over fixedIn fallback
  • Add pluggable version selection strategies: closestCoverageStrategy (default — prefers same-major patches) and highestStrategy (original behavior) (TC-5555)

Implements TC-5414

Test plan

  • 42 tests passing (16 remediation + 15 strategy + 11 remediate)
  • closestCoverageStrategy picks 2.13.9.Final-redhat-00003 over 3.8.6.1 for quarkus-resteasy
  • highestStrategy picks 3.8.6.1 for the same scenario
  • trustedContent always preferred regardless of strategy
  • Verified against real RHTPA backend with da-examples pom.xml

🤖 Generated with Claude Code

Add the `remediate` CLI command that orchestrates the full remediation
pipeline: discover manifests → scan via DA backend → extract
remediations with priority resolution → apply version updates to
manifest files or show dry-run preview.

Supports pom.xml (Maven) and libs.versions.toml (Gradle) manifests,
directory-mode recursive discovery, provider/source filtering, and
bundled/per-dependency report grouping.

Exit codes: 0 = success, 1 = error, 2 = dry-run with remediations found.

Implements TC-5414

Assisted-by: Claude Code
@sourcery-ai

sourcery-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a new remediate CLI command that orchestrates manifest discovery, DA backend analysis, remediation extraction, and either dry-run reporting or in-place manifest updates for Maven and Gradle TOML manifests, with provider/source filtering, grouping options, and CI-friendly exit codes, and introduces a dedicated remediation orchestration module with comprehensive tests.

Sequence diagram for the new remediate CLI remediation pipeline

sequenceDiagram
    actor User
    participant CLI as cli_remediate_handler
    participant Remediation as runRemediation
    participant Env as getCustom
    participant Provider as match
    participant Analysis as analysis.requestStack
    participant Extract as extractRemediations
    participant Report as generateReport
    participant MavenUpdater as updateMavenVersions
    participant TomlUpdater as updateTomlVersions
    participant Backend as Trustify_DA_backend

    User->>CLI: remediate path --dry-run/--apply
    CLI->>Remediation: runRemediation(path, options)

    Remediation->>Env: getCustom(TRUSTIFY_DA_BACKEND_URL, null, opts)
    Env-->>Remediation: backendUrl

    Remediation->>Provider: match(manifestPath, availableProviders, opts)
    Provider-->>Remediation: provider

    Remediation->>Analysis: analysis.requestStack(provider, manifestPath, backendUrl, false, opts)
    Analysis->>Backend: requestStack
    Backend-->>Analysis: analysisReport
    Analysis-->>Remediation: analysisReport

    Remediation->>Extract: extractRemediations(analysisReport, options)
    Extract-->>Remediation: remediations

    alt apply mode and maven manifest
        Remediation->>MavenUpdater: updateMavenVersions(content, versionChanges)
        MavenUpdater-->>Remediation: { applied, content }
        Remediation->>FS: writeFileSync(manifestPath, content)
    else apply mode and toml manifest
        Remediation->>TomlUpdater: updateTomlVersions(content, versionChanges)
        TomlUpdater-->>Remediation: { applied, content }
        Remediation->>FS: writeFileSync(manifestPath, content)
    end

    Remediation->>Report: generateReport(allRemediations, { groupBy, dryRun })
    Report-->>Remediation: report

    Remediation-->>CLI: { exitCode, output }
    CLI->>Console: console.log(output)
    CLI->>Process: process.exit(exitCode)
Loading

File-Level Changes

Change Details Files
Introduce remediate CLI subcommand wired to a new remediation orchestration module.
  • Register a new remediate <path> command in the CLI with options for dry-run, apply, providers, sources, and group-by.
  • Wire the CLI handler to invoke runRemediation and map its result to console output and process exit codes.
  • Update CLI usage text to include the new remediate command.
src/cli.js
Implement remediation orchestration for Maven and Gradle TOML manifests, including discovery, analysis, remediation extraction, and application/dry-run reporting.
  • Define supported manifest types (pom.xml, *.versions.toml, libs.versions.toml) and delegate version updates to existing Maven and TOML updater utilities.
  • Add recursive directory scanning that discovers supported manifests while skipping irrelevant directories such as node_modules and .git.
  • Resolve the DA backend URL from configuration and construct analysis options with provider/source filters.
  • Match appropriate providers for each manifest and call analysis.requestStack to obtain analysis reports.
  • Extract remediations from analysis reports, apply updates via manifest-type-specific updaters in apply mode, and aggregate all remediations for reporting.
  • Generate human-readable remediation reports with support for dependency- and bundle-level grouping, and implement exit-code semantics: 0 for success/no remediations or applied changes, 2 for dry-run with remediations.
  • Handle unsupported manifest types and missing backend configuration with explicit errors, and skip manifests whose provider matching fails.
src/remediate.js
Add comprehensive tests covering remediation orchestration behavior and CLI-level semantics via runRemediation.
  • Introduce helper fixtures for building minimal analysis reports and temporary directory/manifests (pom.xml and libs.versions.toml).
  • Test dry-run behavior to ensure proposed changes are shown while files remain unmodified and exit code 2 is returned when remediations exist.
  • Test apply mode behavior for Maven and TOML manifests, including idempotency of repeated runs and correct version updates.
  • Test directory mode discovery and processing of multiple manifests, including the no-manifest-found case with exit code 0.
  • Verify provider filtering is propagated to analysis options and that different exit-code scenarios (no remediations, dry-run with remediations, unsupported manifests) behave as expected.
  • Validate group-by behavior by asserting bundled report markers in the output.
test/remediate.test.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • Consider validating the CLI flags so --dry-run and --apply cannot be used together (or have a clearly defined precedence), e.g., via yargs conflicts/implies or an explicit check in runRemediation, to avoid ambiguous behavior.
  • In runRemediation, match() failures for specific manifests are silently swallowed; it may be more user-friendly to surface which manifests were skipped (e.g., collecting and reporting them in the final output) so users understand why some files weren’t remediated.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider validating the CLI flags so `--dry-run` and `--apply` cannot be used together (or have a clearly defined precedence), e.g., via yargs `conflicts`/`implies` or an explicit check in `runRemediation`, to avoid ambiguous behavior.
- In `runRemediation`, `match()` failures for specific manifests are silently swallowed; it may be more user-friendly to surface which manifests were skipped (e.g., collecting and reporting them in the final output) so users understand why some files weren’t remediated.

## Individual Comments

### Comment 1
<location path="src/cli.js" line_range="482" />
<code_context>
+			normalize: true,
+		}
+	).options({
+		'dry-run': {
+			alias: 'd',
+			type: 'boolean',
</code_context>
<issue_to_address>
**issue (bug_risk):** Clarify interaction between --dry-run and --apply to avoid surprising destructive changes.

If a user passes both `--dry-run` and `--apply`, the command still performs writes because `apply` is handled independently of `dryRun`. This can violate the expectation that `--dry-run` is always non-destructive. Please either make the flags mutually exclusive (e.g., `yargs.conflicts('dry-run', 'apply')`) or ensure `dryRun` takes precedence in `runRemediation` so no writes happen when `dryRun` is true.
</issue_to_address>

### Comment 2
<location path="src/remediate.js" line_range="138-141" />
<code_context>
+			continue
+		}
+
+		const analysisReport = await analysis.requestStack(provider, manifestPath, url, false, opts)
+		const remediations = extractRemediations(analysisReport, {
+			providerPriority: providers ? providers.split(',') : undefined,
+		})
+
</code_context>
<issue_to_address>
**suggestion:** Normalize provider list values to avoid issues with whitespace in comma-separated input.

`providers.split(',')` will return values with leading whitespace (e.g., `['redhat', ' lightwell']` for `--providers 'redhat, lightwell'`), which can break matching if exact IDs are expected. Consider normalizing with trimming and filtering, e.g. `providers.split(',').map(p => p.trim()).filter(Boolean)`.

```suggestion
		const analysisReport = await analysis.requestStack(provider, manifestPath, url, false, opts)

		const providerPriority = providers
			? providers
					.split(',')
					.map(p => p.trim())
					.filter(Boolean)
			: undefined

		const remediations = extractRemediations(analysisReport, {
			providerPriority,
		})
```
</issue_to_address>

### Comment 3
<location path="test/remediate.test.js" line_range="318-320" />
<code_context>
+		})
+	})
+
+	suite('error handling', () => {
+		/** Verifies that unsupported manifest types throw an error. */
+		test('throws for unsupported manifest type', async () => {
+			const { dir, cleanup } = createTempDir({ 'requirements.txt': 'flask==2.0' })
+			try {
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for the case where provider matching fails and manifests are silently skipped

In `runRemediation`, errors from `match(manifestPath, availableProviders, opts)` are caught and the manifest is silently skipped. Current tests only cover unsupported manifest types (e.g. requirements.txt). Please add a test that stubs `match` to throw for a supported manifest (e.g. pom.xml) and asserts that `runRemediation` completes, reports no remediations, and leaves the file unchanged. This will lock in the intended catch-and-skip behavior.

Suggested implementation:

```javascript
	suite('error handling', () => {

		/** Verifies that provider matching errors are caught and manifests are skipped. */
		test('skips manifests when provider matching fails', async () => {
			const originalPom = '<project><modelVersion>4.0.0</modelVersion></project>'
			const { dir, cleanup } = createTempDir({ 'pom.xml': originalPom })

			try {
				const pomPath = path.join(dir, 'pom.xml')

				// Arrange: make match throw for this supported manifest
				matchStub.throws(new Error('test provider matching failure'))

				// Act: run remediation
				const result = await runRemediation(pomPath, { dryRun: true })

				// Assert: runRemediation completes, reports no remediations, and leaves file unchanged
				expect(result.exitCode).to.equal(0)
				expect(result.output.toLowerCase()).to.include('no remediations')
				expect(readFileSync(pomPath, 'utf8')).to.equal(originalPom)
			} finally {
				cleanup()
			}
		})

```

To wire this up fully, you may also need to:

1. Ensure `readFileSync` is imported at the top of `test/remediate.test.js` if it is not already:
   - For example: `const { readFileSync } = require('fs')`.
2. Confirm that `matchStub` is initialized in the same scope as other tests (likely in a `beforeEach` using `sinon.stub(...)`), so this new test can reuse it.
3. If the "no remediations" message differs in your CLI output, adjust the `expect(result.output.toLowerCase()).to.include('no remediations')` assertion to match the exact wording used elsewhere in the tests for the "no remediations" scenario.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/cli.js
Comment thread src/remediate.js
Comment thread test/remediate.test.js
Comment on lines +318 to +320
suite('error handling', () => {
/** Verifies that unsupported manifest types throw an error. */
test('throws for unsupported manifest type', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add coverage for the case where provider matching fails and manifests are silently skipped

In runRemediation, errors from match(manifestPath, availableProviders, opts) are caught and the manifest is silently skipped. Current tests only cover unsupported manifest types (e.g. requirements.txt). Please add a test that stubs match to throw for a supported manifest (e.g. pom.xml) and asserts that runRemediation completes, reports no remediations, and leaves the file unchanged. This will lock in the intended catch-and-skip behavior.

Suggested implementation:

	suite('error handling', () => {

		/** Verifies that provider matching errors are caught and manifests are skipped. */
		test('skips manifests when provider matching fails', async () => {
			const originalPom = '<project><modelVersion>4.0.0</modelVersion></project>'
			const { dir, cleanup } = createTempDir({ 'pom.xml': originalPom })

			try {
				const pomPath = path.join(dir, 'pom.xml')

				// Arrange: make match throw for this supported manifest
				matchStub.throws(new Error('test provider matching failure'))

				// Act: run remediation
				const result = await runRemediation(pomPath, { dryRun: true })

				// Assert: runRemediation completes, reports no remediations, and leaves file unchanged
				expect(result.exitCode).to.equal(0)
				expect(result.output.toLowerCase()).to.include('no remediations')
				expect(readFileSync(pomPath, 'utf8')).to.equal(originalPom)
			} finally {
				cleanup()
			}
		})

To wire this up fully, you may also need to:

  1. Ensure readFileSync is imported at the top of test/remediate.test.js if it is not already:
    • For example: const { readFileSync } = require('fs').
  2. Confirm that matchStub is initialized in the same scope as other tests (likely in a beforeEach using sinon.stub(...)), so this new test can reuse it.
  3. If the "no remediations" message differs in your CLI output, adjust the expect(result.output.toLowerCase()).to.include('no remediations') assertion to match the exact wording used elsewhere in the tests for the "no remediations" scenario.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as suggestion — proposes additional test coverage for provider matching failure skip behavior. No documented convention or established codebase pattern requires this specific test. No sub-task created.

@codecov-commenter

codecov-commenter commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.30380% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.33%. Comparing base (329b3cf) to head (70afab2).

Files with missing lines Patch % Lines
src/remediate.js 94.28% 10 Missing ⚠️
src/remediation.js 94.32% 8 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #604      +/-   ##
==========================================
+ Coverage   91.22%   91.33%   +0.10%     
==========================================
  Files          42       43       +1     
  Lines        9175     9471     +296     
  Branches     1624     1695      +71     
==========================================
+ Hits         8370     8650     +280     
- Misses        805      821      +16     
Flag Coverage Δ
unit-tests 91.33% <94.30%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/remediation.js 90.21% <94.32%> (+1.67%) ⬆️
src/remediate.js 94.28% <94.28%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ruromero

ruromero commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

[sdlc-workflow/verify-pr] Re: @sourcery-ai[bot] review —

  1. --dry-run/--apply conflict — Classified as code change request (upgraded from suggestion) — this matches project convention: yargs conflicts: is used in 6 places across image/stack/stack-batch commands. Sub-task TC-5536 created.
  2. Surface skipped manifests — Classified as suggestion — no documented convention or established codebase pattern for surfacing skipped items. No sub-task created.

@ruromero

ruromero commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Verification Report for TC-5414 (commit 464608d)

Check Result Details
Review Feedback WARN 3 code change requests (1 original + 2 upgraded from suggestions) → 2 sub-tasks created (TC-5536, TC-5537)
Root-Cause Investigation SKIPPED Both issues are minor implement-task sibling parity gaps; no systemic fix needed
Scope Containment PASS PR files match task spec; src/analysis.js was modified in prerequisite PR
Diff Size PASS 598 lines (183 src + 59 cli + 356 test) proportionate for CLI orchestration command
Commit Traceability PASS Commit 464608d references TC-5414 in body
Sensitive Patterns PASS No secrets detected in 598 added lines
CI Status PASS All 5 checks pass (lint/test Node 22 & 24, Sourcery, PR title, commit messages)
Acceptance Criteria PASS 8/8 criteria met
Test Quality PASS All 11 tests documented, no repetitive patterns, Eval Quality: N/A
Test Change Classification ADDITIVE New test file (356 lines, 11 tests)
Verification Commands PASS npm run lint and npm test pass in CI

Overall: WARN

Two review feedback items require code changes before merge:

  1. TC-5536--dry-run and --apply flags are not mutually exclusive. Passing both silently writes files then returns exit code 2. Fix: add conflicts: 'apply' to match the existing yargs pattern.
  2. TC-5537providers.split(',') does not trim whitespace. Fix: use .map(p => p.trim()).filter(Boolean) to match workspace.js/java_maven.js patterns.

This comment was AI-generated by sdlc-workflow/verify-pr v0.13.8.

…ovider list

- Add yargs conflicts declaration between --dry-run and --apply flags,
  matching the existing pattern used by image/stack/stack-batch commands
- Guard the apply block with `if (apply && !dryRun)` as defense-in-depth
- Normalize provider list with .trim().filter(Boolean) to handle whitespace
  in comma-separated input, matching workspace.js and java_maven.js patterns

Implements TC-5536, TC-5537

Assisted-by: Claude Code
@ruromero
ruromero requested a review from a-oren August 5, 2026 13:59
Yargs conflicts declaration does not work with boolean options that
have default:false — yargs treats both as "present" since they always
exist in argv. Removing the defaults matches the sibling pattern
(html/summary options have no default).

Fixes TC-5536

Assisted-by: Claude Code
…tent

The DA backend returns fixedIn as an array of version strings (e.g.,
['3.8.6.1']), not as a PURL. The extractor was silently skipping these
because PackageURL.fromString() failed on the array. Now constructs a
PURL from the dependency ref when fixedIn is an array.

Also adds trustedContent preference: when both trustedContent and fixedIn
exist for the same dependency, the trustedContent version is preserved
during merge rather than being overridden by a higher fixedIn version.
This ensures Red Hat patches (e.g., 2.17.1.redhat-00002) are preferred
over major version jumps (e.g., 3.0.0).

Fixes TC-5414

Assisted-by: Claude Code
@ruromero
ruromero requested a review from Strum355 August 6, 2026 11:13
Comment thread src/remediate.js Outdated
* @returns {string}
* @throws {Error} if TRUSTIFY_DA_BACKEND_URL is unset
*/
function resolveBackendUrl(opts) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Other commands in cli.js (like license) call selectTrustifyDABackend(opts) for URL resolution. The remediate handler delegates to runRemediation(), which calls resolveBackendUrl() using getCustom('TRUSTIFY_DA_BACKEND_URL', ...). These are different codepaths. It's a consistency gap. The fix would be to import and call selectTrustifyDABackend(opts) instead of the custom resolveBackendUrl(), which would also eliminate the duplicated code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as code change request — sub-task TC-5557 created to address this feedback.

Comment thread src/cli.js Outdated
Comment on lines +496 to +500
describe: 'Comma-separated provider list (e.g., redhat,lightwell)',
},
sources: {
type: 'string',
describe: 'Comma-separated source list',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Based on the pattern from other commands like component, stack, image, and stack-batch, the descriptions should be:

  - providers: 'Comma-separated list of vulnerability providers (env: TRUSTIFY_DA_PROVIDERS)'
  - sources: 'Comma-separated list of vulnerability sources (env: TRUSTIFY_DA_SOURCES)'

The PR currently has:

  • providers: 'Comma-separated provider list (e.g., redhat,lightwell)'
  • sources: 'Comma-separated source list'

So both TRUSTIFY_DA_PROVIDERS and TRUSTIFY_DA_SOURCES env var references are missing, and the wording style differs from every other command.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as code change request — sub-task TC-5558 created to address this feedback.

Comment thread src/remediate.js
* @param {string} dirPath - absolute path to the directory
* @returns {string[]} array of absolute paths to supported manifest files
*/
function discoverManifests(dirPath) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Other common directories that should likely be skipped: target/, build/, .gradle/, .mvn/, vendor/.
A pom.xml inside target/ is a generated artifact, not a source manifest.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as suggestion — proposes additional directory exclusions (target/, build/, .gradle/, .mvn/, vendor/). Valid improvement but no documented convention or established pattern requires these specific exclusions. No sub-task created.

Comment thread src/remediation.js Outdated
return fixedIn
}
if (Array.isArray(fixedIn) && fixedIn.length > 0) {
const version = fixedIn[0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When fixedIn is an array, only the first element is used (fixedIn[0]). If the array contains multiple fix versions, the rest are silently ignored.
Should we pick the highest version instead of the first, consistent with how processIssueRemediation already resolves conflicts via compareVersions()?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as suggestion — this concern is already addressed by commit 0171316 (TC-5555) which adds pluggable version selection strategies. The default closestCoverageStrategy picks the best version from the fixedIn array based on major version proximity, not just fixedIn[0]. No sub-task created.

Comment thread src/remediate.js Outdated
const resolvedPath = path.resolve(targetPath)

let manifestPaths
const stat = fs.statSync(resolvedPath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If resolvedPath doesn't exist, this throws a raw Node.js error, so the user sees that raw ENOENT message instead of something friendlier

Suggested change
const stat = fs.statSync(resolvedPath)
let stat
try {
stat = fs.statSync(resolvedPath)
} catch {
throw new Error(`Path not found: ${resolvedPath}`)
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

[sdlc-workflow/verify-pr] Classified as code change request — sub-task TC-5559 created to address this feedback.

Add a strategy interface with two methods (selectVersion, resolveConflict)
to extractRemediations. Ship two built-in strategies:

- closestCoverageStrategy (default): prefers same-major-version patches
  over cross-major jumps. For quarkus-resteasy@2.13.5.Final, now
  recommends 2.13.9.Final-redhat-00003 instead of 3.8.6.1.

- highestStrategy: original behavior, always picks highest version
  for maximum CVE coverage.

API consumers can override via options.versionStrategy.

Implements TC-5555

Assisted-by: Claude Code
Stop passing dryRun to generateReport so both modes show the full
per-dependency security report instead of a simplified table for
dry-run.

Implements TC-5414

Assisted-by: Claude Code
@ruromero

ruromero commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Verification Report for TC-5414 (commit 39294df)

Check Result Details
Review Feedback WARN 6 code change requests (3 from sourcery-ai + 3 from a-oren) → 5 sub-tasks created (TC-5536, TC-5537, TC-5557, TC-5558, TC-5559); 2 suggestions acknowledged
Root-Cause Investigation N/A New sub-tasks from human reviewer — no systemic root cause
Scope Containment WARN PR covers TC-5414 + TC-5555 scope (5 files); src/analysis.js from task spec handled in prerequisite PR
Diff Size PASS 932 lines across 5 files proportionate for CLI command + strategies + tests
Commit Traceability PASS All 6 commits reference TC-5414, TC-5536, TC-5537, or TC-5555
Sensitive Patterns PASS No secrets detected
CI Status PASS All 5 checks pass (lint/test Node 22 & 24, Sourcery, PR title, commit messages)
Acceptance Criteria PASS 8/8 TC-5414 criteria met
Test Quality PASS All 42 tests documented, no repetitive patterns, Eval Quality: N/A
Test Change Classification ADDITIVE New test files (test/remediate.test.js + strategy tests in test/remediation.test.js)
Verification Commands PASS npm run lint and npm test pass in CI

Overall: WARN

Three new code change requests from a-oren requiring fixes:

  1. TC-5557 — Replace custom resolveBackendUrl() with selectTrustifyDABackend() to match other commands
  2. TC-5558 — Align --providers/--sources option descriptions with other CLI commands (include env var references)
  3. TC-5559 — Add friendly error message for non-existent path (wrap fs.statSync in try/catch)

Two suggestions acknowledged without sub-tasks:

  • Directory exclusions (target/, build/, etc.) — valid but no established convention
  • fixedIn array version selection — already addressed by TC-5555's pluggable strategies

This comment was AI-generated by sdlc-workflow/verify-pr v0.13.8.

- Use selectTrustifyDABackend() instead of custom resolveBackendUrl()
  to match other CLI commands (TC-5557)
- Align --providers/--sources option descriptions with other commands,
  include env var references (TC-5558)
- Add friendly error message for non-existent path instead of raw
  ENOENT (TC-5559)

Implements TC-5557, TC-5558, TC-5559

Assisted-by: Claude Code
@ruromero

ruromero commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

FYI I have tested it locally and this is an example output with --dry-run

## Security Update: io.quarkus:quarkus-resteasy 2.13.5.Final → 2.13.9.Final-redhat-00003

**Provider:** rhtpa | **Source:** redhat-csaf

### Vulnerabilities resolved

| CVE | Severity | Advisory |
| --- | --- | --- |
| CVE-2025-1634 | HIGH | - |
| CVE-2023-6267 | HIGH | - |
| CVE-2023-5675 | HIGH | - |

## Security Update: org.apache.logging.log4j:log4j-core 2.17.1 → 2.17.1.redhat-00002

**Provider:** rhtpa | **Source:** osv-github

### Vulnerabilities resolved

| CVE | Severity | Advisory |
| --- | --- | --- |
| CVE-2026-34480 | MEDIUM | - |
| CVE-2025-68161 | MEDIUM | - |
| CVE-2026-34477 | MEDIUM | - |
| CVE-2021-45105 | MEDIUM | - |

## Security Update: org.springframework.boot:spring-boot 2.7.18 → 2.7.18.rhlw-00004

**Provider:** rhtpa | **Source:** osv-github

### Vulnerabilities resolved

| CVE | Severity | Advisory |
| --- | --- | --- |
| CVE-2025-22235 | HIGH | - |
| CVE-2026-40973 | HIGH | - |

Comment thread src/cli.js
Comment on lines +488 to +493
apply: {
alias: 'a',
type: 'boolean',
desc: 'Apply changes to manifest files',
conflicts: 'dry-run',
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What happens if neither apply nor dry-run are passed? Feels like apply should be the default

Comment thread src/remediate.js
* @param {string} dirPath - absolute path to the directory
* @returns {string[]} array of absolute paths to supported manifest files
*/
function discoverManifests(dirPath) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I havent looked at existing implementations too much, so maybe theres no overlap, but it feels like we're slowly amassing a number of functions that do somewhat similar things (workspace/manifest discovery), is there anything we could re-use instead of having this function or are all the implementations different enough that they warrant their own functions?

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.

4 participants