CI is red on `main` and on every open PR (including the Dependabot PRs
#1531, #1552–#1554) with:
```
panic: file requires newer Go version go1.27 (application built with go1.26)
golangci-lint exit with code 2
```
## Cause
`actions/setup-go` with `go-version: 'stable'` started resolving to
**go1.27.0**. The pinned golangci-lint **v2.11.4** release binary is
built with **go1.26**, and its `go/types` refuses to typecheck source
files declaring a newer language version. Nothing in the failing PRs is
at fault.
## Fix
Bump to **golangci-lint v2.13.1**, whose release binaries are built with
go1.27, so linting works under `stable` again.
That bump also pulls in newer linter versions, which surface 57 findings
this tree did not have before. Handled as follows:
| Linter | Count | Resolution |
|---|---|---|
| `goconst` | 50 | 39 were repeated literals in `_test.go` files —
goconst started counting those in v2.12.0 (golangci-lint#6480 exposed
`ignore-tests`). Fixture data like `"1.0.0"` (89 occurrences) is not a
missing constant, so `goconst.ignore-tests: true`. The other 11 were
real duplication in production code (OpenAPI tags, the `bearer` security
scheme name, `$ref`, `http`/`https`) and became named constants. |
| `gosec` G710 | 1 | **True positive, fixed.** See below. |
| `staticcheck` SA1019 | 6 | Go 1.26 deprecated `ecdsa.PublicKey.X/Y`
and `PrivateKey.D`. Annotated with the same targeted `nolint` +
rationale already used for `ScalarBaseMult` in
`cmd/publisher/auth/common.go`. |
### The open redirect is real
`TrailingSlashMiddleware` copied the entire `*url.URL` before rewriting
the path. For an absolute-form request URI — legal for a Go HTTP server
to parse, e.g. `GET http://evil.com/foo/ HTTP/1.1` — `r.URL.Scheme` and
`r.URL.Host` are populated, and they were echoed straight back in the
`Location` header. The redirect target is now built from the cleaned
path and query alone. The existing `path.Clean` hardening for
GHSA-v8vw-gw5j-w7m6 (protocol-relative `//evil.com/`) is unchanged.
### Also
Switched `gomodguard` → `gomodguard_v2`; the former is deprecated as of
v2.12.0 and was emitting a warning on every run.
## Verification
- `golangci-lint v2.13.1` against this tree: **0 issues**, no
deprecation warnings
- `make validate`: all validations passed
- `go build ./...` and tests for every touched package pass (DB-backed
tests need Postgres; left to CI)
## Note on migrating the ecdsa key paths
Moving those six sites to `PublicKey.Bytes` /
`ParseUncompressedPublicKey` / `crypto/ecdh` touches key parsing and
signature verification. It should be its own reviewed PR rather than
riding along on a CI unblock. Happy to open one.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The HTTP namespace-verification fetcher's `isBlockedIP` composed its
blocklist from Go stdlib's `Is*` helpers plus a manual CGNAT range. That
covers IPv4 cloud metadata, RFC1918, ULA, and link-local — but Go's
classification helpers don't recognise IPv6 prefix families that embed
or tunnel to arbitrary IPv4:
- 2002::/16 RFC 3056 6to4 — bits 16-47 are an arbitrary IPv4
- 64:ff9b::/96 RFC 6052 NAT64 well-known — low 32 bits are IPv4
- 64:ff9b:1::/48 RFC 8215 NAT64 local-use — same IPv4-embedding shape
- fec0::/10 RFC 3879 site-local (deprecated, still routed by
some stacks)
On hosts with 6to4 routing or a NAT64 next-hop (the default on
IPv6-only GKE node pools, AWS IPv6-only EC2, Azure IPv6 VMs with
NAT64, and many corporate DNS64/NAT64 networks), an attacker who
controls DNS for a publisher domain can cause the registry to dial
`169.254.169.254`, `10.0.0.0/8`, etc. via the IPv6 wrapper. Same
SSRF class as GHSA-56c3-vfp2-5qqj on n8n-mcp.
Define the four prefixes via a new `mustCIDR` helper (panic on
malformed CIDR at init so a typo can't fail-open silently) and iterate
them in `isBlockedIP` after the stdlib gates. Refactor the existing
CGNAT IIFE to use the same helper for consistency. IPv4-mapped IPv6
(::ffff:0:0/96) needs no special-casing: Go's `Is*` helpers honour the
`To4()` fast-path, so e.g. `::ffff:10.0.0.1` is already classified as
`IsPrivate`.
Adds 17 test cases to `TestIsBlockedIP` covering each new prefix
family, IPv4-mapped IPv6 sanity checks, and just-outside-the-range
positive controls (`2001::1`, `2003::1`, `64:ff9c::1`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## What this does
Fixes an open redirect plus a handful of small, mostly-invisible
hardening changes around it.
## What's in it
- **The fix**: trailing-slash redirects no longer let `//evil.com/`
redirect off-host.
- **SSRF block** on the DNS/HTTP namespace verification — refuses to
dial loopback, RFC1918, link-local, multicast, CGNAT, or
IP-literal/single-label \"domains\".
- **Slow-client guard**: `ReadTimeout` and `IdleTimeout` on the HTTP
server (no `WriteTimeout` — would risk cutting off legitimate
multi-package publishes).
- **Defence-in-depth headers** on the UI (`/`) and 404 responses.
- **5xx responses** stop leaking raw DB error text to clients (logged
server-side instead).
- **Search filter** stops treating `%` and `_` as wildcards.
- **Validators**: PyPI/NuGet identifiers URL-escaped before fetching;
MCPB no longer follows redirects.
- **Misc**: `Version` field bounded to 255 chars; logs quote user input;
`BlockedNamespaces` denylist now catches subdomain claimants.
Each change is its own commit so they can be reviewed (or reverted)
independently.
## Test plan
- [ ] CI green
- [ ] Synthetic publish in staging for each package type (npm, pypi,
nuget, oci, mcpb) — confirms validator changes don't regress
- [ ] Quick browser check of `/` after deploy to confirm UI still loads
with the new CSP
- [ ] `curl https://<host>//evil.com/` — confirm the open redirect is
closed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
Follow up from #658 addressing the feedback from @tadasant
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR adds a new v0.1 API version.
The goal is to mark v0.1 as a "freeze" point while keeping v0 as the
moving target for new features so integrators can start building on top
of something stable(r).
Details:
* This change is quite simple - we re-register the same handlers twice,
once as `v0` and once as `v0.1`
* Going forward, we'll keep v0.1 frozen while we add changes either on
top of both (non-breaking) or we'll have to branch if breaking changes
are necessary
* We all acknowledge this is not exactly best-practice, but it gives us
a way to gather feedback and allow clients to start integrating with the
API without breaking them (**note** this implies a follow up PR that
would branch the handlers used in v0.1).
* For integrators/clients: please provide your feedback - what works
well, what doesn't so we can take this into account going towards GA.
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
No, this just adds a new API version alongside the existing one.
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
Fixes: #649
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
<!-- Provide a brief summary of your changes -->
## Motivation and Context
Currently the `dns` and `http` based auth flows share code well in the
publisher tool. However there is a lot of duplication in the auth
endpoint. This on it's own is not a problem but it does make
enhancements to the proof record (the `v=MCPv1; ...` string) harder to
keep in sync.
This PR adds a `common.go` file to share logic between the two.
This supports work I am prototyping for
https://github.com/modelcontextprotocol/registry/issues/482.
## How Has This Been Tested?
Added new test cases for the permissions. Ran them before and after the
refactoring to ensure no behavior change.
Ran existing UTs.
Ran the integration tests.
Manually exercised the flows locally.
## Breaking Changes
None.
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
## Motivation and Context
Replace server-only `http.MaxBytesReader(nil, ...)` with
`io.LimitReader` to cap the fetched body to 4KB in key fetcher. This
avoids passing a `nil` ResponseWriter and aligns with Go idioms. If more
than 4KB is received, return a clear error. Previously `MaxBytesReader`
returned a misleading `http: request body too large` error, which is
server-oriented. With this change the error becomes `HTTP auth key
response too large`.
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
Add tests covering success, oversized bodies, connection failure,
non-200 status, and read failures. This covers almost all lines of
`FetchKey` method of `DefaultHTTPKeyFetcher`. For tests, I also added a
new initialiser `NewDefaultHTTPKeyFetcherWithClient` so that the HTTP
client could be modified for test use.
Tests use `httptest.NewTLSServer` plus a helper to construct a client
pinned to the test server.
`auth` package test coverage increased from 56.8% to 59.3%.
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
None.
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
<!-- Provide a brief summary of your changes -->
## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR eliminates duplicate struct definitions by migrating
the publisher tool to use canonical types from pkg/model, creating a
single source of truth across the codebase. The publisher tool
previously had its own copies of structs that were duplicated, ie.
ServerJSON, Package, Repository, VersionDetail, EnvironmentVariable,
RuntimeArgument and a few others which was not optimal
**Changes in the PR:**
- Refactored the structs to single canonical types located at pkg/model
- Refactor the implementation to use/build canonical structures directly
- Remove all duplicate definitions (5 structs eliminated)
- Publisher now uses model.ServerJSON, model.Package, model.Repository,
etc.
- Renamed some of the structs to better reflect their use case, i.e.
ServerJSON instead of ServerDetail
- Fixes an issue where the Remote property was missing from the previous
publisher copy of the Server
**Benefits:**
- Single source of truth - Schema changes in one place
- Type safety - Compiler catches mismatches
- Enhanced validation - Field constraints and bson tags
- Zero breaking changes - Same CLI interface and JSON output
- Future-proof - New canonical fields automatically available
Supersedes: #217 and #240
## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
Locally
## Breaking Changes
<!-- Will users need to update their code or configurations? -->
No
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
---------
Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Add HTTP-based authentication to prove domain ownership, similar to
https://github.com/modelcontextprotocol/registry/pull/238's DNS
authentication but using HTTPS well-known URIs instead of DNS TXT
records.
Fixes#280
## Motivation and Context
Some organizations don't have easy access to DNS records, but can easily
deploy web assets. They would therefore prefer to be able to complete a
HTTP challenge rather than a DNS one to authenticate to the registry.
This would in some way mirror ACME challenges: where you can do a DNS-01
or a HTTP-01 challenge.
## How Has This Been Tested?
- Tested locally by stubbing out the HTTP getter
- Added unit tests
## Breaking Changes
None
## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
## Additional context
I concern I had when building this that it might expose us to SSRF
attacks.
I think on reflection this is not a significant problem:
- We only GET a fixed file `.well-known/mcp-registry-auth` (redirects
disallowed) after sanitizing the domain, and then also don't expose the
response to the user. This means the user shouldn't be able to hit
anything very interesting.
- The information they might get is what domains/servers exist in the
network. Our infrastructure is already all open-source, so attackers
aren't getting any additional real info by prodding at the environment
this way.