VYPR
Moderate severityNVD Advisory· Published Jul 8, 2025· Updated Jul 8, 2025

Sensitive log retrieval in Juju

CVE-2025-53512

Description

The /log endpoint on a Juju controller lacked sufficient authorization checks, allowing unauthorized users to access debug messages that could contain sensitive information.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/juju/jujuGo
< 0.0.0-20250619024904-402ff008dcc20.0.0-20250619024904-402ff008dcc2

Affected products

1

Patches

2
c91a1f404695

fix: ensure that users wanting to use debug-log via the /log

https://github.com/juju/jujuwallyworldJun 13, 2025via ghsa
3 files changed · +41 28
  • apiserver/apiserver.go+11 6 modified
    @@ -48,6 +48,7 @@ import (
     	coredatabase "github.com/juju/juju/core/database"
     	"github.com/juju/juju/core/lease"
     	"github.com/juju/juju/core/multiwatcher"
    +	"github.com/juju/juju/core/permission"
     	"github.com/juju/juju/core/presence"
     	"github.com/juju/juju/core/resources"
     	"github.com/juju/juju/internal/worker/syslogger"
    @@ -707,15 +708,19 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) {
     	healthHandler := srv.monitoredHandler(http.HandlerFunc(srv.healthHandler), "health")
     	logStreamHandler := srv.monitoredHandler(newLogStreamEndpointHandler(httpCtxt), "logstream")
     	embeddedCLIHandler := srv.monitoredHandler(newEmbeddedCLIHandler(httpCtxt), "commands")
    +	var debuglogAuth httpcontext.CompositeAuthorizer = []authentication.Authorizer{
    +		tagKindAuthorizer{names.MachineTagKind, names.ControllerAgentTagKind},
    +		controllerAdminAuthorizer{
    +			controllerTag: systemState.ControllerTag(),
    +		},
    +		modelPermissionAuthorizer{
    +			perm: permission.ReadAccess,
    +		},
    +	}
     	debugLogHandler := srv.monitoredHandler(newDebugLogDBHandler(
     		httpCtxt,
     		httpAuthenticator,
    -		tagKindAuthorizer{
    -			names.MachineTagKind,
    -			names.ControllerAgentTagKind,
    -			names.UserTagKind,
    -			names.ApplicationTagKind,
    -		},
    +		debuglogAuth,
     	), "log")
     	pubsubHandler := srv.monitoredHandler(newPubSubHandler(httpCtxt, srv.shared.centralHub), "pubsub")
     	logSinkHandler := logsink.NewHTTPHandler(
    
  • apiserver/debuglog_db_test.go+18 2 modified
    @@ -14,6 +14,7 @@ import (
     
     	apitesting "github.com/juju/juju/apiserver/testing"
     	"github.com/juju/juju/apiserver/websocket/websockettest"
    +	"github.com/juju/juju/core/permission"
     	"github.com/juju/juju/rpc/params"
     	"github.com/juju/juju/testing/factory"
     )
    @@ -59,16 +60,17 @@ func (s *debugLogDBSuite) TestUnitLoginsRejected(c *gc.C) {
     	conn, _, err := s.dialWebsocketInternal(c, nil, header)
     	c.Assert(err, jc.ErrorIsNil)
     
    -	websockettest.AssertJSONError(c, conn, "authorization failed: tag kind unit not valid")
    +	websockettest.AssertJSONError(c, conn, "authorization failed: permission denied")
     	websockettest.AssertWebsocketClosed(c, conn)
     }
     
     var noResultsPlease = url.Values{"maxLines": {"0"}, "noTail": {"true"}}
     
    -func (s *debugLogDBSuite) TestUserLoginsAccepted(c *gc.C) {
    +func (s *debugLogDBSuite) TestUserLoginAccepted(c *gc.C) {
     	u := s.Factory.MakeUser(c, &factory.UserParams{
     		Name:     "oryx",
     		Password: "gardener",
    +		Access:   permission.ReadAccess,
     	})
     	header := jujuhttp.BasicAuthHeader(u.Tag().String(), "gardener")
     	conn, _, err := s.dialWebsocketInternal(c, noResultsPlease, header)
    @@ -80,6 +82,20 @@ func (s *debugLogDBSuite) TestUserLoginsAccepted(c *gc.C) {
     	c.Assert(result.Error, gc.IsNil)
     }
     
    +func (s *debugLogDBSuite) TestUserLoginRejected(c *gc.C) {
    +	u := s.Factory.MakeUser(c, &factory.UserParams{
    +		Name:        "oryx",
    +		Password:    "gardener",
    +		NoModelUser: true,
    +	})
    +	header := jujuhttp.BasicAuthHeader(u.Tag().String(), "gardener")
    +	conn, _, err := s.dialWebsocketInternal(c, noResultsPlease, header)
    +	c.Assert(err, jc.ErrorIsNil)
    +
    +	websockettest.AssertJSONError(c, conn, "authorization failed: permission denied")
    +	websockettest.AssertWebsocketClosed(c, conn)
    +}
    +
     func (s *debugLogDBSuite) TestMachineLoginsAccepted(c *gc.C) {
     	m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
     		Nonce: "foo-nonce",
    
  • go.sum+12 20 modified
    @@ -18,8 +18,7 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW
     cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
     cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
     cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
    -cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
    -cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
    +cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
     cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
     cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
     cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
    @@ -952,21 +951,17 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
     go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
     go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
     go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
    -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
    -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
    +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
     go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
    -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
    -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
    +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
     go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
    -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
    -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
    +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
     go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
    -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
    -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
    -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
    -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
    -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
    -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
    +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
    +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
    +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
    +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
    +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
     go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
     go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
     go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
    @@ -1294,8 +1289,7 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR
     google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
     google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
     google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
    -google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0=
    -google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4=
    +google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs=
     google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
     google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
     google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
    @@ -1349,8 +1343,7 @@ google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRx
     google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
     google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
     google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
    -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
    -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
    +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
     google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
     google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
     google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
    @@ -1372,8 +1365,7 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
     google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
     google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
     google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
    -google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
    -google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
    +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
     google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
     google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
     google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
    
402ff008dcc2

fix: ensure that users wanting to use debug-log via the /log

https://github.com/juju/jujuwallyworldJun 13, 2025via ghsa
2 files changed · +31 3
  • apiserver/apiserver.go+13 1 modified
    @@ -44,6 +44,7 @@ import (
     	"github.com/juju/juju/core/cache"
     	"github.com/juju/juju/core/lease"
     	"github.com/juju/juju/core/multiwatcher"
    +	"github.com/juju/juju/core/permission"
     	"github.com/juju/juju/core/presence"
     	"github.com/juju/juju/core/resources"
     	"github.com/juju/juju/feature"
    @@ -689,9 +690,20 @@ func (srv *Server) endpoints() ([]apihttp.Endpoint, error) {
     	healthHandler := http.HandlerFunc(srv.healthHandler)
     	logStreamHandler := newLogStreamEndpointHandler(httpCtxt)
     	embeddedCLIHandler := newEmbeddedCLIHandler(httpCtxt)
    +	var debuglogAuth httpcontext.CompositeAuthorizer = []httpcontext.Authorizer{
    +		tagKindAuthorizer{names.MachineTagKind, names.ControllerAgentTagKind},
    +		controllerAdminAuthorizer{
    +			st: systemState,
    +		},
    +		modelPermissionAuthorizer{
    +			userAccess: systemState.UserPermission,
    +			perm:       permission.ReadAccess,
    +		},
    +	}
     	debugLogHandler := newDebugLogDBHandler(
     		httpCtxt, srv.authenticator,
    -		tagKindAuthorizer{names.MachineTagKind, names.ControllerAgentTagKind, names.UserTagKind, names.ApplicationTagKind})
    +		debuglogAuth,
    +	)
     	pubsubHandler := newPubSubHandler(httpCtxt, srv.shared.centralHub)
     	logSinkHandler := logsink.NewHTTPHandler(
     		newAgentLogWriteCloserFunc(httpCtxt, srv.logSinkWriter, &srv.apiServerLoggers),
    
  • apiserver/debuglog_db_test.go+18 2 modified
    @@ -14,6 +14,7 @@ import (
     
     	apitesting "github.com/juju/juju/apiserver/testing"
     	"github.com/juju/juju/apiserver/websocket/websockettest"
    +	"github.com/juju/juju/core/permission"
     	"github.com/juju/juju/rpc/params"
     	"github.com/juju/juju/testing/factory"
     )
    @@ -59,16 +60,17 @@ func (s *debugLogDBSuite) TestUnitLoginsRejected(c *gc.C) {
     	conn, _, err := s.dialWebsocketInternal(c, nil, header)
     	c.Assert(err, jc.ErrorIsNil)
     
    -	websockettest.AssertJSONError(c, conn, "authorization failed: tag kind unit not valid")
    +	websockettest.AssertJSONError(c, conn, "authorization failed: permission denied")
     	websockettest.AssertWebsocketClosed(c, conn)
     }
     
     var noResultsPlease = url.Values{"maxLines": {"0"}, "noTail": {"true"}}
     
    -func (s *debugLogDBSuite) TestUserLoginsAccepted(c *gc.C) {
    +func (s *debugLogDBSuite) TestUserLoginAccepted(c *gc.C) {
     	u := s.Factory.MakeUser(c, &factory.UserParams{
     		Name:     "oryx",
     		Password: "gardener",
    +		Access:   permission.ReadAccess,
     	})
     	header := jujuhttp.BasicAuthHeader(u.Tag().String(), "gardener")
     	conn, _, err := s.dialWebsocketInternal(c, noResultsPlease, header)
    @@ -80,6 +82,20 @@ func (s *debugLogDBSuite) TestUserLoginsAccepted(c *gc.C) {
     	c.Assert(result.Error, gc.IsNil)
     }
     
    +func (s *debugLogDBSuite) TestUserLoginRejected(c *gc.C) {
    +	u := s.Factory.MakeUser(c, &factory.UserParams{
    +		Name:        "oryx",
    +		Password:    "gardener",
    +		NoModelUser: true,
    +	})
    +	header := jujuhttp.BasicAuthHeader(u.Tag().String(), "gardener")
    +	conn, _, err := s.dialWebsocketInternal(c, noResultsPlease, header)
    +	c.Assert(err, jc.ErrorIsNil)
    +
    +	websockettest.AssertJSONError(c, conn, "authorization failed: permission denied")
    +	websockettest.AssertWebsocketClosed(c, conn)
    +}
    +
     func (s *debugLogDBSuite) TestMachineLoginsAccepted(c *gc.C) {
     	m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
     		Nonce: "foo-nonce",
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.