Skip to content

fix(docker): add nil-client guards to all service adapters (#623)#639

Merged
CybotTM merged 2 commits into
mainfrom
fix/issue-623
May 13, 2026
Merged

fix(docker): add nil-client guards to all service adapters (#623)#639
CybotTM merged 2 commits into
mainfrom
fix/issue-623

Conversation

@CybotTM

@CybotTM CybotTM commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Defense-in-depth hardening from the #619 security review: every public method on every *ServiceAdapter in core/adapters/docker/ (Container, Exec, Image, Event, Network, Swarm, System) now returns the new sentinel ErrNilDockerClient instead of panicking with a nil-pointer dereference if the embedded SDK client is nil.

The newClientFromSDK constructor always wires a non-nil client, so this is only reachable through hand-rolled adapter values (test fixtures or wiring bugs) — but the guards convert what would otherwise be a panic in a hot goroutine into a branchable, actionable failure that callers can route via errors.Is.

Implementation

  • New core/adapters/docker/errors.go exports ErrNilDockerClient.
  • Each adapter gets a private checkClient() helper (per-receiver-type, so the lint-friendly errors.Is works without cross-package coupling) called as the first line of every public method.
  • Channel-returning methods (ContainerServiceAdapter.Wait, EventServiceAdapter.Subscribe) cannot return an error, so they push the sentinel onto errCh and close both channels synchronously without launching a goroutine — caller's select loop exits cleanly.
  • ExecServiceAdapter.Run / .Create get the new client guard ahead of the existing ErrNilExecConfig / ErrNoExecOutputWriter input-validation guards (per the issue acceptance: "every public method"). The two pre-existing tests in exec_test.go that pinned those input-validation guards are updated to use a loopback SDK client so the original behavior remains observable.

Methods guarded (35 total)

Adapter Methods
ContainerServiceAdapter Create, Start, Stop, Remove, Inspect, List, Wait, Logs, CopyLogs, Kill, Pause, Unpause, Rename, Attach (14)
ExecServiceAdapter Create, Start, Inspect, Run (4)
ImageServiceAdapter Pull, PullAndWait, List, Inspect, Remove, Tag, Exists (7)
EventServiceAdapter Subscribe, SubscribeWithCallback (2)
NetworkServiceAdapter Connect, Disconnect, List, Inspect, Create, Remove (6)
SwarmServiceAdapter Create, Inspect, List, Remove, ListTasks, WaitForTask, WaitForServiceTasks (7)
SystemServiceAdapter Info, Ping, Version, DiskUsage (4)

(Pull request body lists 44 because some adapters share the table-driven test row count differently — repo source of truth.)

Tests

  • New core/adapters/docker/nilclient_test.go — one table-driven regression test per adapter that iterates each public method on a zero-valued *ServiceAdapter{} and asserts each call returns ErrNilDockerClient via errors.Is. Each top-level test wraps in failOnPanic from test(docker): cover adapter edge cases identified in #610 #619's testhelpers_test.go to fail loudly if any method ever regresses to a nil-pointer dereference.
  • Channel-returning paths additionally assert both channels are closed after error delivery so the caller's select loop exits cleanly without leaking a goroutine.

Test plan

  • go test -race ./core/adapters/docker/ — all green (5.6s)
  • golangci-lint run --timeout=3m ./core/adapters/docker/... — 0 issues
  • go vet ./... — clean across the project
  • Pre-existing TestExecServiceAdapter_Create_NilConfig and TestExecServiceAdapter_Run_NilWritersNonTTY still pin their original input-validation contracts (now via loopback SDK client)

Fixes #623
Refs #619

Defense-in-depth: every public method on every *ServiceAdapter in
core/adapters/docker/ (Container, Exec, Image, Event, Network, Swarm,
System) now returns the new sentinel ErrNilDockerClient instead of
panicking with a nil-pointer dereference if the embedded SDK client is
nil. Each adapter gets a private checkClient() helper called as the first
line of every public method to keep the boilerplate localized.

The newClientFromSDK constructor always wires a non-nil client, so this
is only reachable through hand-rolled adapter values (test fixtures or
wiring bugs) — but the guards convert what would otherwise be a panic in
a hot goroutine into a branchable, actionable failure that callers can
route via errors.Is.

Channel-returning methods (ContainerServiceAdapter.Wait,
EventServiceAdapter.Subscribe) cannot return an error, so they push the
sentinel onto errCh and close both channels synchronously without
launching a goroutine. ExecServiceAdapter.Run and .Create get the new
client guard ahead of the existing ErrNilExecConfig / ErrNoExecOutputWriter
input-validation guards; the two pre-existing tests in exec_test.go that
exercised those guards are updated to use a loopback SDK client so the
input-validation behavior they pin remains observable.

Includes a table-driven regression test per adapter that iterates each
public method on a zero-valued *ServiceAdapter{} and asserts each call
returns ErrNilDockerClient via errors.Is, wrapped in failOnPanic to fail
loudly if any method ever regresses to a nil-pointer dereference.

Fixes #623
Refs #619

Signed-off-by: Sebastian Mendel <[email protected]>
Copilot AI review requested due to automatic review settings May 13, 2026 16:26
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests labels May 13, 2026
github-actions[bot]
github-actions Bot previously approved these changes May 13, 2026

@github-actions github-actions 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.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

@github-actions

Copy link
Copy Markdown

✅ Mutation Testing Results

Mutation Score: 89.13% (threshold: 60%)

✨ Good job! Mutation score meets the threshold.

What is mutation testing?

Mutation testing measures test quality by introducing small changes (mutations) to the code and checking if tests detect them. A higher score means better test effectiveness.

  • Killed mutants: Tests caught the mutation (good!)
  • Survived mutants: Tests missed the mutation (needs improvement)

@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.18033% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.60%. Comparing base (d014841) to head (70b6de9).

Files with missing lines Patch % Lines
core/adapters/docker/service.go 94.44% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #639      +/-   ##
==========================================
+ Coverage   87.38%   87.60%   +0.22%     
==========================================
  Files          88       88              
  Lines       10722    10844     +122     
==========================================
+ Hits         9369     9500     +131     
+ Misses       1112     1103       -9     
  Partials      241      241              
Flag Coverage Δ
integration 87.58% <99.18%> (+0.20%) ⬆️
unittests 84.66% <96.72%> (+0.51%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Pull request overview

This PR hardens the Docker SDK service adapters in core/adapters/docker/ against nil embedded SDK clients by returning a shared sentinel error (ErrNilDockerClient) instead of panicking, and adds regression tests to ensure the guard behavior stays intact.

Changes:

  • Introduces ErrNilDockerClient and adds per-adapter checkClient() guards to public adapter methods.
  • Updates channel-returning APIs (ContainerServiceAdapter.Wait, EventServiceAdapter.Subscribe) to synchronously deliver the sentinel error and close channels when the client is nil.
  • Adds table-driven nil-client regression tests and updates existing exec tests to keep their original input-validation assertions.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
core/adapters/docker/errors.go Adds exported ErrNilDockerClient sentinel error.
core/adapters/docker/container.go Adds nil-client guard to all public container adapter methods; special handling for Wait.
core/adapters/docker/exec.go Adds nil-client guard ahead of existing exec input-validation checks.
core/adapters/docker/event.go Adds nil-client guard; Subscribe now synchronously errors+closes channels when client is nil.
core/adapters/docker/image.go Adds nil-client guard to image adapter methods.
core/adapters/docker/network.go Adds nil-client guard to network adapter methods.
core/adapters/docker/service.go Adds nil-client guard to swarm/service adapter methods.
core/adapters/docker/system.go Adds nil-client guard to system adapter methods.
core/adapters/docker/nilclient_test.go Adds regression tests ensuring nil-client calls never panic and return the sentinel.
core/adapters/docker/exec_test.go Adjusts existing tests to use a loopback SDK client so pre-existing validation behavior remains testable.
CHANGELOG.md Documents the new defense-in-depth nil-client behavior.

Comment thread core/adapters/docker/errors.go Outdated
Comment thread core/adapters/docker/nilclient_test.go Outdated
Comment thread CHANGELOG.md Outdated

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements a defense-in-depth strategy across all Docker service adapters to prevent nil-pointer dereference panics when the embedded SDK client is nil. It introduces a new sentinel error, 'ErrNilDockerClient', and adds a 'checkClient' guard method to all public adapter methods. The changes include comprehensive regression tests in 'nilclient_test.go' and updates to existing tests to ensure proper error handling. The reviewer suggested refactoring the repetitive 'checkClient' implementation to reduce code duplication and improve maintainability.

Comment thread core/adapters/docker/container.go
- Update ErrNilDockerClient godoc to reference the actual constructor
  surface (NewClient / NewClientWithConfig + per-service accessors)
  instead of nonexistent New*Adapter constructors. (Copilot)
- Mirror the Subscribe channel-close assertion in the Wait nil-client
  test: assert both respCh and errCh are closed after the sentinel,
  so a regression that delivered the error but left the channels open
  cannot pass the test. (Copilot)
- Fix CHANGELOG link convention: link the PR via /pull/639 with the
  fixes-the-issue reference, matching the surrounding entries.
  (Copilot)

Refs #623, #639.

Signed-off-by: Sebastian Mendel <[email protected]>
@sonarqubecloud

Copy link
Copy Markdown

@github-actions github-actions 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.

Automated approval for maintainer PR

All automated quality gates passed. See SECURITY_CONTROLS.md for compensating controls.

@CybotTM CybotTM added this pull request to the merge queue May 13, 2026
Merged via the queue into main with commit 1770064 May 13, 2026
27 checks passed
@CybotTM CybotTM deleted the fix/issue-623 branch May 13, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Defense-in-depth: nil-receiver / nil s.client guards in Docker adapters

2 participants