Skip to content

fix(cli): surface post-scan write failures and sanitize diagnostics.json - #134

Merged
PunGrumpy merged 5 commits into
mainfrom
fix/handoff-robustness
Sep 18, 2026
Merged

PunGrumpy merged 5 commits into
mainfrom
fix/handoff-robustness

Conversation

@PunGrumpy

Copy link
Copy Markdown
Owner

Description

Three problems on the post-scan path, all about failures that never reached the user.

The wizard swallowed every write failure. The whole interactive block ran inside catch {} with an "Ignore prompt errors" comment, but the prompts never reject. What that block actually caught was every write on the path: the GitHub workflow scaffold, .docker-doctor/, the .gitignore entry, the skill install. On a read-only checkout the user answered yes, nothing was written, and the CLI exited 0 without a word.

$ docker-doctor .          # in a directory with no write permission
...
  ⚠ Post-scan step failed: EACCES: permission denied, mkdir '/srv/app/.docker-doctor'
$ echo $?
1

Scan-incomplete still outranks it, so a run that could not read a Dockerfile keeps reporting 2 rather than 1.

diagnostics.json was the one agent-facing surface that skipped the sanitizer. The per-rule .txt files run every message and path through sanitizeMessage and sanitizePath, which flatten control characters and cap the length. diagnostics.json, the file the handoff prompt names first and tells the agent to read in full, was written straight from the report. Rule messages quote Dockerfile lines verbatim, so that was exactly the gap the sanitizer exists to close. help is authored by the rules, never by scanned content, so it stays as written.

Five error paths called process.exit. The success paths already assign process.exitCode and return, which lets Node flush a pending write first. process.exit does not wait, so piping --json into a file and hitting an error could truncate the message the CI job then prints. action.yml redirects both streams to files, so it sits squarely in that case. Signal handling keeps process.exit: 130 and 143 are meant to be immediate.

Related Issues

N/A. Found while tracing what the agent handoff writes and what it tells the agent to read.

Checklist

  • I've reviewed my code
  • I've written tests
  • I've generated a changeset file, if this changes a published package
  • I've updated the docs, if necessary

Screenshots (if applicable)

N/A, CLI output only.

Additional Notes

Tests: a diagnostics-dir case asserting the JSON and the .txt report agree on a message carrying a newline and an ANSI escape, and a CLI case pinning rules explain on an unknown rule at exit 1 now that it no longer calls process.exit. The existing "large --json output over a pipe is not truncated" test guards the third change. CLI: 40 pass.

Both halves of the wizard change were also exercised by hand under a pty: the happy path prints exactly what it did before and exits 0, and a chmod 555 checkout produces the message above and exits 1.

Two judgment calls to flag:

  • runInteractiveWizard returns a boolean rather than the caller reading process.exitCode back, so the precedence between a wizard failure and an incomplete scan is explicit at the call site.
  • POST_SCAN_FAILURE_EXIT_CODE is a named constant next to the existing SCAN_FAILURE_EXIT_CODE so the precedence comment has somewhere to live. Bare 1 literals elsewhere are untouched.

Considered and left out: prompting before .docker-doctor/ is recreated or .gitignore is appended. The directory is tool-owned by name and the menu already says what happens, so a second prompt costs more than it protects.

The handoff prompt points the agent at .docker-doctor/ and tells it to
read the full report there. The per-rule .txt files run every message
and path through sanitizeMessage and sanitizePath, which flatten
control characters and cap the length; diagnostics.json, the file the
prompt names first, was written straight from the report.

Rule messages quote Dockerfile lines verbatim, so that was the one
agent-facing surface where scanned content arrived unflattened and
unbounded, which is exactly what the sanitizer exists to prevent. help
text is authored by the rules, so it stays as written.
The interactive wizard ran inside catch {} with an "Ignore prompt
errors" comment, but the prompts never reject. What that block actually
caught was every write in the post-scan path: the GitHub workflow
scaffold, .docker-doctor/, the .gitignore entry, the skill install.

On a read-only checkout the user answered yes, nothing was written, and
the CLI exited 0 without a word. Name the failure and fold it into the
exit code. Scan-incomplete still outranks it, so a run that could not
read a Dockerfile keeps reporting 2 rather than 1.
The success paths already assign process.exitCode and return, which
lets Node flush a pending write before it exits. The five error paths
called process.exit instead, which does not wait: piping --json into a
file and hitting an error could truncate the message the CI job then
prints. The GitHub Action redirects both streams to files, so it sits
squarely in that case.

Signal handling keeps process.exit: 130 and 143 are meant to be
immediate.
@changeset-bot

changeset-bot Bot commented Sep 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 79e30ca

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@docker-doctor/cli Patch
docker-doctor-kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

The latest Docker Doctor scan for this pull request. Learn more about Docker Doctor.

File Status Issues Updated
Dockerfile Error 1 error, 2 info Sep 18, 2026 2:14am UTC

Score: 84 / 100 · Good

3 issues

Dockerfile

  • error Dockerfile:2 Potential secret found in ENV: 'DB_PASSWORD'. no-secrets-in-env
  • info Dockerfile:1 No HEALTHCHECK instruction found. require-healthcheck
  • info Dockerfile:1 No LABEL metadata was found in this Dockerfile. require-labels

Scanned by Docker Doctor for commit 79e30ca.

@vercel

vercel Bot commented Sep 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docker-doctor Ready Ready Preview Sep 18, 2026 2:16am UTC

@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 52 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 4c4d87c6-e097-4e66-ba29-3d07ede867d2

📥 Commits

Reviewing files that changed from the base of the PR and between d9b9a43 and 79e30ca.

📒 Files selected for processing (5)
  • .changeset/handoff-robustness.md
  • packages/docker-doctor/src/agents/diagnostics-dir.ts
  • packages/docker-doctor/src/cli.ts
  • packages/docker-doctor/test/cli.test.ts
  • packages/docker-doctor/test/diagnostics-dir.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@baymiai

baymiai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Security review: nothing to raise.

Read the wizard error handling and the diagnostics.json sanitization, which closes the one agent‑facing surface that previously bypassed it. The sanitizer caps length and flattens control characters, exactly as the per‑rule .txt files already do.

@PunGrumpy PunGrumpy added kind: bug Something isn't working pkg: cli Command-line interface in packages/docker-doctor labels Sep 17, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@docker-doctor/cli@134

commit: 79e30ca

@PunGrumpy
PunGrumpy merged commit 66816aa into main Sep 18, 2026
18 of 19 checks passed
@PunGrumpy
PunGrumpy deleted the fix/handoff-robustness branch September 18, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind: bug Something isn't working pkg: cli Command-line interface in packages/docker-doctor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant