#3847·kubescape

feat(fix): honour --output-dir for file-based reports by writing fixed copies instead of editing in place

Author: Dreamstick9Created Sep 16, 2026Updated Sep 16, 2026

Description

kubescape fix --output-dir currently applies only to cluster scan reports. For a file-based report (directory, git repo, or single file scan) the flag is documented as ignored: since #3767 it warns and the manifests are still edited in place.

The flag reads as "write the fixed manifests here instead of the default destination", and that meaning is just as useful for file scans — arguably more so, since the default there is rewriting the user's own files. This proposes honouring --output-dir for file-based reports: write the fixed copies into the directory, mirroring the scanned tree, and leave the originals untouched.

Raised at this week's community meeting; opening this to pin the design before the PR.

Current behaviour

$ kubescape fix results.json --output-dir ./patches --no-confirm
[warning] --output-dir has no effect when fixing manifest files; it applies to
cluster scan reports. The files recorded in the report are fixed in place
[info] Fixed 7 of 24 flagged control instances across 1 file(s).
$ ls ./patches
$

The originals are modified; the directory stays empty.

Proposed behaviour

With --output-dir set on a file-based report:

  • Each file that would have been edited in place is instead written to <output-dir>/<path relative to the scan root>. The scan root is the one the report recorded (localRootPath / basePath / the single file's directory), and the relative path is the one the report already carries per resource, so a multi-input scan and a single-file scan both land where you'd expect.
  • The originals are not touched.
  • Multi-document YAML files stay intact: the write is per source file, not per resource, exactly as in-place fixing is today.
  • The confirmation prompt is skipped — it exists to guard in-place edits, and there are none. This matches the cluster path.
  • A non-empty output directory is refused unless --no-confirm is passed. This is the guard the cluster path already applies, reused as-is.
  • An output directory that resolves to the scan root itself is refused outright, regardless of --no-confirm: writing there would overwrite the originals, which is the one thing the flag exists to avoid.
  • --dry-run writes nothing, as it does everywhere else.
  • The summary line names the destination: Wrote 3 patched file(s) to ./patches. Unfixed controls are reported as planned rather than applied, since nothing has been applied to the originals.

Without --output-dir, nothing changes: file-based reports are fixed in place with the confirmation prompt as today.

The #3767 warning is removed, since the situation it warned about no longer exists.

Compatibility

--output-dir shipped in v4.0.14 (2026-09-09) with the cluster-only semantics, so the "ignored for file-based reports" behaviour is in a tagged release. What changes for whom:

  • Cluster scan users: nothing. The cluster path is not modified.
  • File scan users who never passed --output-dir: nothing.
  • File scan users who did pass --output-dir: until #3767 it silently did nothing; since #3767 it warns and does nothing. Under this proposal it writes copies and stops editing in place. Anyone in that group was passing a no-op flag — there is no working behaviour to break, only an inert one to give meaning to — but it is a behaviour change and should be called out in the release notes.

Scope

Code:

  • core/pkg/fixhandler/fixhandler.goApplyChanges gains a destination: carry the report's relative path on ResourceFixInfo, and when OutputDir is set write to <OutputDir>/<relative path> (creating parents) with a containment check, instead of back over the source.
  • core/core/fix.go — drop the #3767 warning; branch on OutputDir for file reports: skip the prompt, run the non-empty / scan-root guards, and report where the files went.
  • core/core/clusterfix.go — lift the non-empty-directory guard out of writeClusterFixes so both paths share it.
  • cmd/fix/fix.go — flag help text and the examples block ("this command will change your files in-place" gets an "unless --output-dir").
  • core/meta/datastructures/v1/fix.goFixInfo.OutputDir comment.

Docs (each currently says "cluster scans only"):

  • docs/cli-reference.md — flags table, examples, and the note block (which currently documents the #3767 warning).
  • docs/getting-started.md — flags table.
  • README.md — an example under "Auto-Fix".

Tests:

  • The two tests added in #3767 that pin "warns and is ignored" are replaced.
  • New: copies written and originals untouched; tree mirrored for a nested path; multi-document file preserved; non-empty directory refused without --no-confirm; output dir equal to scan root refused; --dry-run writes nothing; cluster path unchanged.

Not in scope

  • Changing the cluster path's per-resource output layout.
  • Any interaction with --base-path beyond what already applies to the source paths.

Related

  • #3733 / #3767 — the warning this replaces
  • #3705 — where --output-dir was introduced for cluster reports