Three improvements identified in the Principal Engineer pass but
deliberately deferred:
1. Test fidelity. Split executePlan into newPlanExecutor (constructs
the executor seeded from observed state) and (*planExecutor).run
(walks the DAG). Production callers go through executePlan
unchanged. TestExecutePlanRemoveContainerDropsFromCache now uses
newPlanExecutor + run, exercising the same errgroup, done-channel
and group-tracker wiring as production instead of a hand-rolled
loop over executeNode.
2. //nolint:unused chain. The three preserved helpers
(reconciler.prompt, planRecreateVolume, servicesUsingVolume) each
carried a separate "kept for future" comment. Consolidate the
rationale on the reconciler.prompt field doc and point the helper
nolint directives there, so a future cleanup is a single grep.
3. Concurrency test. Add TestExecutePlanConcurrentRemovesCacheCoherence
which builds N independent Stop→Remove chains in one plan; the
errgroup fans them out across goroutines that all hit
containersByService under the mutex. Passes under -race. Failure
would expose a missing or incorrect lock.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: Guillaume Lours <[email protected]>
Two related changes to the executor, plus the small cleanups they
attracted in review:
* Rename node consults a planner-set CreateNodeID instead of walking
ancestors. The old execRenameContainer searched node.DependsOn for a
CreateContainer result and fell back to a recursive walk through the
group chain. That worked only by convention; a future op with cross-
node data needs would have to rediscover or copy the pattern. Now the
rename op carries an explicit CreateNodeID int set at plan time and
the executor reads pctx[CreateNodeID].ContainerID directly. The
recursive findCreatedIDInChain is gone.
* Stop re-listing containers on every create. execCreateContainer used
to call getContainersByService(ctx, projectName) — a fresh
ContainerList per create — to resolve service references at execute
time. The executor now holds a live containersByService view seeded
from ObservedState (via observed.containersByService()) and grown as
OpCreateContainer nodes complete, so service references resolve from
memory. On OpRemoveContainer the removed container is dropped from
the view via slices.DeleteFunc, so a dependent's create that resolves
network_mode: service:x against the just-removed container cannot
pick up a stale ID (Containers.sorted() orders by canonical name and
would otherwise return the removed container).
* Defensive slices.Clone of op.Service.VolumesFrom in execCreateContainer.
resolveServiceReferences mutates VolumesFrom in place, and the
shallow struct copy of *op.Service still shares the backing array.
Single-execution-per-node makes it safe today, but the clone removes
the trap for any future parallel-execution mode.
* Operation gains a CreateNodeID int (not a *PlanNode pointer) to avoid
a structural cycle between Operation and PlanNode. OperationType
values are pinned to explicit integers so adding an op in the middle
cannot silently shift the others.
* execRenameContainer carries two checks so a missing CreateNodeID and
an empty produced ID are distinguishable in logs. Both are programmer
invariants (prefixed "internal:").
* containersByServiceFromObserved moved from a package-level helper to
a method on *ObservedState.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: Guillaume Lours <[email protected]>
ensureProjectVolumes already prompts the user when a volume's config hash
diverges from the compose file (create.go:1626) and recreates it on confirm.
The reconciler ran after ensureProjectVolumes and prompted again with the
exact same message — so a user who declined the first prompt was asked the
same question a second time.
Drop the prompt + recreate call from reconcileVolumes(). Recreation of
diverged volumes stays owned by ensureProjectVolumes; the reconciler only
plans the creation of missing volumes. If the user declined recreation,
the existing container's mounts still match the existing volume name and
hasVolumeMismatch correctly returns false, so containers are not falsely
flagged as obsolete.
Keep the supporting infrastructure available for future use, when
divergence detection migrates fully into the reconciler:
- reconciler.prompt field
- prompt parameter on reconcile()
- planRecreateVolume function (//nolint:unused)
- servicesUsingVolume function (//nolint:unused)
- noPrompt test helper
The reframed test (TestReconcileVolumes_DivergedIsIgnored) asserts the
new contract: a diverged volume produces no plan operations from the
reconciler.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: Guillaume Lours <[email protected]>
- plan.go: pin OperationType constants to explicit values so adding an op
in the middle doesn't shift the others.
- executor.go: remove the meaningless `var _ = getContainerProgressName`
line — same-package functions are always accessible.
- reconcile.go: fix the stale switch-default comment that contradicted
the case clause above it.
- reconcile.go: drop the local `serviceLabel` const that shadowed
`api.ServiceLabel` and use the shared constant.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: Guillaume Lours <[email protected]>
- Fix ContainerReplaceLabel detection: use op.Inherited != nil (not
op.Container) as signal for recreate in execCreateContainer
- Use observed network name (not desired) for DisconnectNetwork and
RemoveNetwork operations, in case the name changed
- Use observed volume name (not desired) for RemoveVolume operations
- Update reconciliation.md with 3 new lessons learned (7.8, 7.9, 7.10)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: Nicolas De Loof <[email protected]>
- Remove the `convergence` struct and `newConvergence` constructor
- Extract `resolveServiceReferences` as a standalone function taking
`map[string]Containers` instead of a method on convergence
- Add `getContainersByService` helper on composeService
- Update run.go and executor.go to use the new standalone function
- Remove dead code: `getObservedState`, `setObservedState`
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Signed-off-by: Nicolas De Loof <[email protected]>
When a service's build context is a remote git/HTTP URL, the path was
unconditionally added to bake's --allow fs.read= entitlements. On Windows,
bake then tries to evaluate the URL as a local filesystem path and fails
because `https:` is invalid path syntax (colon is reserved for drive
letters).
Apply the same gitutil.ParseGitRef + "://" check already used for
additional_contexts so that remote contexts are skipped from the fs.read
allowlist.
Fixes#13815
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: Nicolas De loof <[email protected]>
Link to the corresponding `docker container logs` equivalents, which
contain more details on the accepted formats and use.
The container logs documentation still needs some updates to provide
per-flag sections, so follow-ups can be made once those are done.
Signed-off-by: Sebastiaan van Stijn <[email protected]>
Replace the "flag any literal env var" check with a key-name heuristic
backed by the upstream DefangLabs keyword detector (password, secret,
token, api_key, …), and convert the hard error into a prompt matching
the existing checkForBindMount / checkForSensitiveData UX. --with-env
silences the env prompt; literal config.content gets its own prompt.
The previous check flagged benign vars like LOG_LEVEL=info, blocking
the 99% case, while still missing low-entropy real secrets the
existing secret-detector skips (MYSQL_ROOT_PASSWORD=toto slips through
on entropy ~1.5).
Refs: docker/compose#13394
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: Guillaume Lours <[email protected]>
Pass the active project name as the appId query parameter on the
docker-desktop://dashboard/logs deep link, both from the post-command
hint (compose up -d, compose logs) and the interactive nav menu
('l' key during compose up). The hook subprocess re-runs compose-go's
project loader so the name matches what the parent computed; it skips
the appId when -p, -f, --project-directory, --workdir, or --env-file
is set, since the hook payload does not carry their values. docker
logs stays unfiltered: the CLI hook contract does not expose the
positional container id.
Signed-off-by: Guillaume Lours <[email protected]>
Docker Desktop is removing the "Enable Logs view" beta setting, so drop
the /app/settings check and rely on /features alone. With the setting
gate gone, the compose hook subprocess would print the Logs view hint
regardless of LogsTab; add a flag check in handleHook. Consolidate
engine-label discovery and feature-flag evaluation into internal/desktop.
Signed-off-by: Guillaume Lours <[email protected]>
The stop lifecycle hook added in 672dc14d2 calls stoppingEvent and
stoppedEvent from pkg/compose/plugins.go, but the helpers had been
removed by the earlier wrapper-cleanup in da530c723. The result was a
broken go build for pkg/compose on upstream/main.
Restore the two helpers alongside the symmetric creating/created and
removing/removed pairs so call sites in plugins.go match the existing
pattern used in the same switch.
Signed-off-by: Guillaume Lours <[email protected]>
A previous change extended IsEmpty() to include `Stop == nil`. For a
provider that advertises only a `stop` block, this caused
commandMetadataIsEmpty to be false in setupPluginCommand, which made
the option-forwarding filter reject every key — silently dropping
all provider options on `up` and `down`.
The Stop-presence signal lives independently in stop.go and the
"stop" case of setupPluginCommand, so reverting the IsEmpty check
to its original semantics is sufficient.
Addresses PR #13779 review feedback.
Signed-off-by: Guillaume Lours <[email protected]>
The previous implementation fetched the provider binary path and
metadata twice per service during `compose stop`: once in stop.go
to gate on the `stop` capability, and again inside runPlugin via
setupPluginCommand.
setupPluginCommand now signals "skip" by returning (nil, nil) when
the requested command is absent from the provider's metadata.
stop.go calls runPlugin directly; the skip-when-unadvertised check
moves into runPlugin.
Addresses PR #13779 review feedback.
Signed-off-by: Guillaume Lours <[email protected]>
Provider-backed services were silently skipped on `docker compose stop`,
leaving external resources running after the user expected the stack to
be paused (e.g. after Ctrl+C during `up --watch`).
Compose now invokes `<provider> compose stop <service>` for providers
that advertise a `stop` block in their `metadata` subcommand output.
Providers that do not advertise stop (or do not implement metadata at
all) are silently skipped, preserving backward compatibility with
existing providers that pre-date this hook.
Closes#13772
Signed-off-by: Guillaume Lours <[email protected]>
- Fix stale image/container reuse across test runs - Add registry readiness check and async removal polling
- Skip multi-arch test when docker driver supports it
- Use t.Cleanup for reliable teardown, fix project name mismatches
- Re-enable 4 previously skipped tests that now pass
Signed-off-by: Guillaume Lours <[email protected]>
The compose process performs OCI artifact fetches in-process via
containerd's docker resolver, whose default transport only honors
HTTP_PROXY/HTTPS_PROXY/NO_PROXY env vars. Users behind PAC-only
corporate proxies hit i/o timeouts on `oci://` includes and on
`compose publish`.
When Docker Desktop is the active engine and exposes httpproxy.sock,
route the resolver through it (PAC-aware). Falls back to the default
transport when DD is unavailable or the socket is missing. Modeled on
docker/mcp-gateway PR #354.
Signed-off-by: Guillaume Lours <[email protected]>
- https://github.com/golang/go/issues?q=milestone%3AGo1.26.3+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.26.2...go1.26.3
This release include 11 security fixes:
- cmd/go: malicious module proxy can bypass checksum database
A malicious module proxy could exploit a flaw in the go command's
validation of module checksums to bypass checksum database validation.
This vulnerability affects any user using an untrusted module proxy
(GOMODPROXY) or checksum database (GOSUMDB).
A malicious module proxy can serve altered versions of the Go toolchain.
When selecting a different version of the Go toolchain than the
currently installed toolchain (due to the GOTOOLCHAIN environment variable,
or a go.work or go.mod with a toolchain line), the go command will download
and execute a toolchain provided by the module proxy. A malicious module
proxy can bypass checksum database validation for this downloaded
toolchain.
Since this vulnerability affects the security of toolchain downloads,
setting GOTOOLCHAIN to a fixed version is not sufficient. You must upgrade
your base Go toolchain.
The go tool always validates the hash of a toolchain before executing it,
so fixed versions will refuse to execute any cached, altered versions of the
toolchain.
The go tool trusts go.sum files to contain accurate hashes of the current
module's dependencies. A malicious proxy exploiting this vulnerability to
serve an altered module will have caused an incorrect hash to be recorded
in the go.sum. Users who have configured a non-trusted GOPROXY can determine
if they have been affected by running "rm go.sum ; go mod tidy ; go mod verify",
which will revalidate all dependencies of the current module.
The specific flaw in more detail:
The go command consults the checksum database to validate downloaded modules,
when a module is not listed in the go.sum file. It verifies that the module hash
reported by the checksum database matches the hash of the downloaded module.
If, however, the checksum database returns a successful response that contains
no entry for the module, the go command incorrectly permitted validation to succeed.
A module proxy may mirror or proxy the checksum database, in which case the go
command will not connect to the checksum database directly. Checksums reported
by the checksum database are cryptographically signed, so a malicious proxy
cannot alter the reported checksum for a module. However, a proxy which returns
an empty checksum response, or a checksum response for an unrelated module,
could cause the go command to proceed as if a downloaded module has been validated.
The go command now properly checks checksum database responses to ensure
that the expected module signature is present, not just that if a signature is
present it matches the expectation.
Thanks to Mundur (https://github.com/M0nd0R) for reporting this issue.
This is CVE-2026-42501 and Go issue https://go.dev/issue/79070.
- net/http/httputil: ReverseProxy forwards queries with more than urlmaxqueryparams parameters
When used with a Rewrite function, or a Director function which parses query parameters,
ReverseProxy sanitizes the forwarded request to remove query parameters which are not
parsed by url.ParseQuery. ReverseProxy did not take ParseQuery's limit on the total number
of query parameters (controlled by GODEBUG=urlmaxqueryparams=N) into account.
This could permit ReverseProxy to forward a request containing a query parameter
that was not visible to the Rewrite function.
For example, the query "a1=x&a2=x&...&a10000=x&hidden=y" could forward the parameter
"hidden=y" while hiding it from the proxy's Rewrite function.
ReverseProxy now avoids forwarding parameters that exceed the ParseQuery limit.
This is CVE-2026-39825 and Go issue https://go.dev/issue/78948.
- net: panic in Dial and LookupPort when handling NUL byte on Windows
The Dial and LookupPort functions would panic on Windows when provided
with an input containing a NUL (0). These functions now return an error
rather than panicking.
This is CVE-2026-39836 and Go issue https://go.dev/issue/79006.
- net/mail: quadratic string concatenation in consumePhrase
Pathological inputs could cause DoS through consumePhrase
when parsing an email address according to RFC 5322.
This is CVE-2026-42499 and Go issue https://go.dev/issue/78987.
- net/mail: quadratic string concatentation in consumeComment
Well-crafted inputs reaching ParseAddress, ParseAddressList,
and ParseDate were able to trigger excessive CPU exhaustion
and memory allocations.
This is CVE-2026-39820 and Go issue https://go.dev/issue/78566.
- cmd/go: "go bug" follows symlinks in predictable temporary filenames
The "go bug" command wrote to two files with predictable names in
the system temporary directory (for example, "/tmp").
An attacker with access to the temporary directory could create a
symlink in one of these names, causing "go bug" to overwrite the
target of the symlink.
The "go bug" command now uses os.MkdirTemp to create a safe
working directory.
Thanks to Harshit Gupta (Mr HAX) for reporting this issue.
This is CVE-2026-39819 and Go issue https://go.dev/issue/78584.
- cmd/go: "go tool pack" does not sanitize output paths
The "go tool pack" subcommand is a minimal version of the Unix ar utility.
It is used by the compiler as an internal tool with known-good inputs.
The "pack" subcommand did not sanitize output filenames.
When invoked to extract a malicious archive file, it could write
files to arbitrary locations on the filesystem.
The "pack" subcommand now refuses to extract files with names
containing any directory components.
Thanks to Harshit Gupta (Mr HAX) for reporting this issue.
This is CVE-2026-39817 and Go issue https://go.dev/issue/78778.
- net/http: infinite loop in HTTP/2 transport when given bad SETTINGS_MAX_FRAME_SIZE
When processing HTTP/2 SETTINGS frames, transport will enter an infinite loop of
writing CONTINUATION frames if it receives a SETTINGS_MAX_FRAME_SIZE with a
value of 0.
This allows potential DoS against a client by a malicious server. HTTP/2
transport now properly checks that the received SETTINGS_MAX_FRAME_SIZE is
valid.
Thanks to Marwan Atia ([email protected]) for reporting this issue.
This is CVE-2026-33814 and Go issue https://go.dev/issue/78476.
- html/template: escaper bypass leads to XSS
If a trusted template author were to write a
tag containing an empty type attribute or a type
attribute with an ASCII whitespace, the execution of
the template would incorrectly escape any data passed
into the block.
Thanks to Mundur (https://github.com/M0nd0R) for reporting this issue.
This is CVE-2026-39826 and Go issue https://go.dev/issue/78981.
- net: crash when handling long CNAME response
When using LookupCNAME with the cgo DNS resolver,
a very long CNAME response could trigger a double-free of C memory
and a crash. The double-free has been fixed.
Thanks to hamayanhamayan for reporting this issue.
This is CVE-2026-33811 and Go issue https://go.dev/issue/78803.
- html/template: bypass of meta content URL escaping causes XSS
CVE-2026-27142 fixed a vulnerability in which URLs were not
correctly escaped inside of a tag's attribute.
If the URL content were to insert ASCII whitespaces around the
= rune inside of the attribute, the escaper would
fail to similarly escape it, leading to XSS.
Dynamic inputs to a tag's attribute are now
whitespace sanitized prior to escaping.
Thanks to Samy Ghannad for reporting this issue.
Signed-off-by: Sebastiaan van Stijn <[email protected]>