Security fixes are issued for the latest minor release line on npm and GHCR. Older minors do not receive patches; upgrading is the supported remediation path.
| Version | Supported |
|---|---|
| 1.x | ✅ — latest minor receives fixes |
| < 1.0 | ❌ — pre-release, do not run in production |
Please do not open a public GitHub issue for security reports.
Report privately via one of:
- GitHub Security Advisories — preferred. https://github.com/ThoTischner/observability-mcp/security/advisories/new
- Email —
ai-solutions-camp@email.de
What to include:
- A description of the issue and its impact.
- Reproduction steps or a proof-of-concept. The smaller the better.
- The version (npm tag or container SHA) you tested against.
- Your preferred credit name and link (or a request to remain anonymous).
You can expect:
- An acknowledgement within 3 business days.
- A triage decision and severity score (CVSS v3.1) within 7 business days.
- A coordinated disclosure timeline — typically 30 days for a fix to ship, longer only if the bug requires upstream changes.
- Credit in the release notes and (with consent) in any published advisory.
In scope:
mcp-server/— the MCP server itself, the Web UI, the/api/*endpoints, the/mcptransport.- Connectors that ship with the server (Prometheus, Loki).
- The Helm chart at
helm/observability-mcp/. - Default Docker image at
ghcr.io/thotischner/observability-mcp.
Out of scope (report to the respective upstream):
- Third-party connectors loaded as plugins.
- Vulnerabilities in
@modelcontextprotocol/sdk,express,hono, Node.js itself. - Configuration mistakes (e.g. running with
MCP_AUTH_TOKENunset on a public network). - Findings from automated scanners without a working PoC.
The server is built with the following defaults:
npm audit --audit-level=highis enforced in CI; transitive vulnerabilities in the MCP SDK are pinned vianpm overrides(seeproject_cve_strategyfor the strategy).- Container runs as a non-root user, no privilege escalation, all capabilities
dropped,
seccompProfile: RuntimeDefaultvia the Helm chart. - npm publish uses
--provenanceso installers can verify build attestation. tools/listoutput is the source of truth; the server does not advertise features that aren't actually wired.
If you operate the server on the public internet, set MCP_AUTH_TOKEN and
front it with a reverse proxy that terminates TLS.
Every release artifact ships with build attestations so operators can verify it came from this repository's CI before installing it.
# Verify the package was built by the published GitHub Actions workflow
npm view @thotischner/observability-mcp dist-tags
npm audit signatures @thotischner/observability-mcpProvenance is also visible on the npm package page under the "Provenance" tab.
# Pull and inspect the source-commit label
docker pull ghcr.io/thotischner/observability-mcp:latest
docker image inspect ghcr.io/thotischner/observability-mcp:latest \
--format '{{ index .Config.Labels "org.opencontainers.image.revision" }}'CI scans every image with Trivy before publishing; high-severity CVEs block the release.
Keyless cosign signing. Every image is signed via Sigstore keyless OIDC (no operator-managed key material). Verify against the GitHub Actions workflow identity:
cosign verify \
--certificate-identity-regexp "^https://github\.com/ThoTischner/observability-mcp/\.github/workflows/docker-publish\.yml@refs/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/thotischner/observability-mcp:latestSBOM attestations. CycloneDX-JSON + SPDX-JSON SBOMs (Syft-generated, in addition to the buildx-embedded SBOM) are attached as cosign attestations, per platform — one CycloneDX + one SPDX predicate is bound to each per-platform digest in the manifest list. Resolve the platform digest first, then verify:
IMAGE=ghcr.io/thotischner/observability-mcp:latest
# Pick the platform you actually run (linux/amd64 or linux/arm64).
PLATFORM=linux/amd64
PER_PLATFORM_DIGEST=$(docker buildx imagetools inspect --raw "$IMAGE" \
| jq -r --arg p "$PLATFORM" '
.manifests[] | select((.platform.os + "/" + .platform.architecture) == $p) | .digest')
# CycloneDX
cosign verify-attestation \
--type cyclonedx \
--certificate-identity-regexp "^https://github\.com/ThoTischner/observability-mcp/\.github/workflows/docker-publish\.yml@refs/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
"ghcr.io/thotischner/observability-mcp@${PER_PLATFORM_DIGEST}" \
| jq -r '.payload | @base64d | fromjson | .predicate' > image-sbom.cdx.json
# SPDX
cosign verify-attestation \
--type spdxjson \
--certificate-identity-regexp "^https://github\.com/ThoTischner/observability-mcp/\.github/workflows/docker-publish\.yml@refs/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
"ghcr.io/thotischner/observability-mcp@${PER_PLATFORM_DIGEST}" \
| jq -r '.payload | @base64d | fromjson | .predicate' > image-sbom.spdx.jsonPer-platform coverage: Syft scans both the amd64 and arm64 variants on the amd64 runner via the
--platformflag (it pulls the right layer set from the manifest list) and attaches the resulting SBOMs as cosign attestations on the matching per-platform digest. The buildx-embedded SBOM remains as well, for tools that read attached SBOMs from the image directly.
The raw SBOM files are also uploaded as workflow artifacts on each release (90-day retention), so an air-gapped operator can pull them from the GitHub Actions run page without round-tripping cosign.
SLSA provenance attestation. buildx attaches SLSA-level provenance
(mode=max) to the OCI image; cosign also verifies that:
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity-regexp "^https://github\.com/ThoTischner/observability-mcp/\.github/workflows/docker-publish\.yml@refs/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/thotischner/observability-mcp:latestThe chart is published twice on every release: the GPG-signed
tarball lives on the GitHub Pages chart repo (the path the
helm repo add command targets), and the same chart is pushed to
GHCR as an OCI artifact and signed with cosign keyless. Either path is
authoritative; pick whichever fits the consumer's posture.
GPG path (default helm repo add). Public key committed at
docs/helm-signing.pub.asc; fingerprint
also advertised on ArtifactHub via artifacthub.io/signKey.
curl -sS https://raw.githubusercontent.com/ThoTischner/observability-mcp/main/docs/helm-signing.pub.asc \
| gpg --import
helm verify ./observability-mcp-<version>.tgzOCI / cosign path. The chart is pushed to
oci://ghcr.io/thotischner/charts/observability-mcp and each version
is cosign-signed by digest via Sigstore keyless OIDC. No GPG keyring
required on the consumer side:
# Pull (or install directly from OCI):
helm pull oci://ghcr.io/thotischner/charts/observability-mcp --version <version>
# Verify the cosign signature against the GitHub Actions workflow identity:
cosign verify \
--certificate-identity-regexp "^https://github\.com/ThoTischner/observability-mcp/\.github/workflows/helm-release\.yml@refs/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/thotischner/charts/observability-mcp:<version>The server only advertises tools it actually implements. The integration
smoke test asserts the exact set of advertised tools on every PR (see
.github/workflows/integration.yml).
If tools/list claims a tool, calling it does what the docs say. If a tool
name is missing from tools/list, it does not exist in that build.