Repository navigation
plugins: make every catalogue plugin run in the service; release v1.1.0 - #33
Merged
Merged
Conversation
Run through the service's own path, 47 of the 80 catalogue plugins never worked. Three causes, all on the service side for 25 of them: - plugarchive refused any archive holding an absolute symlink, and every image dump holds some (/etc/localtime, the loader under lib64). Such links are now skipped; the entrypoint stays refused. The write-through-a-symlink-chain escape (x -> ., l1 -> x/.., l1/evil) is closed by resolving each entry's parent and every created link against the root. - protoc's own plugins need glibc 2.38; the image was bookworm (2.36). The runtime stage is trixie-slim now. glibc is backward compatible; the service binary is static. - plugins inherited the service's cwd, / and unwritable; betterproto writes there. Each run gets a private working directory under plugins_dir/.tmp. Checked against all 80 latest archives in the rebuilt image: 33 -> 58 work, no regressions (same output per plugin). The other 22 exec absolute paths in their wrappers and need a rebuild. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A version now builds only if it also survives what the service does to it: - absolute symlinks pointing at files the bundle carries are rewritten as relative ones (a Debian JRE reads java.security through one), the rest are removed; - the bundle is packed and unpacked with plugarchive, so an archive the service would refuse fails here rather than after a push; - the entrypoint is run in the service's base image (debian:trixie-slim, kept equal to the Dockerfile by a test) as uid 65532, with an empty environment, on a synthetic CodeGeneratorRequest; a CodeGeneratorResponse — even one carrying an error — passes. A version that fails is emptied down to its build.log, so push, register and the build cache no longer see an entrypoint for it. plugin.yaml gains an optional `smoke` block (parameter, skip); flags --runtime-image and --no-smoke. Checked end to end: grpc/go builds and passes; bufbuild/es, whose wrapper still execs /nodejs/bin/node, is refused with that message and leaves only its log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Their wrappers exec'd paths that only existed inside the build image
(/usr/bin/java, /nodejs/bin/node, /app/...); the service runs the unpacked
bundle from plugins_dir on its own base, so none of them could start. Each
wrapper now resolves everything from DIR=${0%/*}:
- JVM: the bundle's own JRE, found as usr/lib/jvm/*/bin/java;
- Node: the bundle's node with the script path;
- Python: the bundle's interpreter under the bundle's own dynamic loader
(glibc or musl), so its libraries never mix with the host's;
- native: the binary beside the wrapper.
Inside the image $0 is /plugin and DIR is empty, so the paths read as before.
bufbuild/connect-kotlin's download stage moves off a bullseye snapshot whose
security pool now 404s.
Rebuilt with smoke checks on: the latest version of all 22, and all 9 versions
of connect-kotlin across its three Dockerfiles, run in debian:trixie-slim.
README and AGENTS.md describe the bundle contract and the build checks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every catalogue plugin now runs in the service: the image moves to trixie, plugarchive skips absolute symlinks and closes the symlink-chain escape, each run gets a private working directory, plugins build verifies every bundle the way the service runs it, and the 22 wrapped plugins resolve paths from their own directory. Minor rather than patch because the runtime base changed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ZergsLaw
enabled auto-merge
October 5, 2026 05:18
govulncheck flagged the OTLP trace exporter (v1.43.0): its config logging can leak endpoint URLs into info logs, and the service calls it from telemetry.Init. Fixed upstream in v1.45.0; the otel modules move together to v1.47.0, which is what go mod tidy settles on with otelgrpc v0.70.0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Run through the service's own path (
plugarchive.Unpack+ exec with an empty env), 47 of the 80 catalogue plugins never worked. Three causes, measured on one archive per plugin and then on all 1743 archives:/etc/localtime, the loader underlib64);Unpackrefused the whole archiveGLIBC_2.38; image was bookworm (2.36)debian:trixie-slim/usr/bin/java,/nodejs/bin/node,/app/…)DIR=${0%/*}Plus: plugins inherited the service's cwd (
/, unwritable) — betterproto writes there.What changed
internal/plugarchive— absolute symlinks are skipped (the entrypoint stays refused); the write-through-a-symlink-chain escape (x -> .,l1 -> x/..,l1/evil) is closed by resolving each entry's parent and every created link against the root.Dockerfile— runtime stagetrixie-slim. The service binary is static; glibc is backward compatible.internal/adapters/registry— each plugin run gets a private working directory underplugins_dir/.tmp, removed afterwards.plugins build— every built version is verified the way the service will run it: in-bundle absolute links made relative, archive round-tripped throughplugarchive, entrypoint smoke-run in the runtime image as uid 65532 with an empty env. A failing version is emptied to itsbuild.log.plugin.yamlgainssmoke: {parameter, skip}; flags--runtime-image,--no-smoke.TestSmokeImageMatchesServiceBasekeeps the smoke image equal to the Dockerfile.registry/— the 22 wrappers (JVM, Node, Python glibc/musl, native) made relocatable;bufbuild/connect-kotlin's download stage moved off a bullseye snapshot that now 404s.Verification
go test -race ./...and golangci-lint clean.easyp-pluginsand verified (size for all 1743, sha256 spot checks).grpc/web,anthropics/connect-rust), 2 start but reject the synthetic request (neoeinstein-prostv0.2.1 wantssource_code_info,mercari-grpc-federationv0.12.0 an option). None fail for environment reasons. Before: 33/80 latest versions worked.Follow-ups (not in this PR)
plugins pack outloses its path when the argument is spelled like the--outflag.smoke.parameterfor the 10 archives above, so a full rebuild does not reject working versions.UpdatePlugin(new sha256) wherever they are registered.🤖 Generated with Claude Code