Skip to content

[false-negative]: SPR-CONFIG-001 misses management.endpoint.<id>.access and mis-states what the wildcard exposes #45

Description

@vianbas

Related: #44. The detection half of this report cannot be implemented until that lands,
because ConfigKeyMetadataTest rejects the key it needs. Found by scanning
halo-dev/halo at a559d15.

Config that is missed

application/src/main/resources/application.yaml:

management:
  endpoints:
    web:
      exposure:
        include: "*"          # line 80, sprig reports this
  endpoint:
    shutdown:
      access: unrestricted    # line 83, sprig says nothing
    heapdump:
      access: unrestricted    # line 85, sprig says nothing
$ sprig scan halo
SPR-CONFIG-001 [HIGH] application/src/main/resources/application-dev.yaml:49  Actuator endpoint(s) exposed: *.
SPR-CONFIG-002 [MEDIUM] application/src/main/resources/application-mariadb.yaml:5  Hardcoded secret in configuration: spring.r2dbc.password.
SPR-CONFIG-002 [MEDIUM] application/src/main/resources/application-mysql.yaml:5  Hardcoded secret in configuration: spring.r2dbc.password.
SPR-CONFIG-002 [MEDIUM] application/src/main/resources/application-postgresql.yaml:5  Hardcoded secret in configuration: spring.r2dbc.password.
SPR-CONFIG-002 [MEDIUM] application/src/main/resources/application.yaml:13  Hardcoded secret in configuration: spring.r2dbc.password.
SPR-CONFIG-001 [HIGH] application/src/main/resources/application.yaml:80  Actuator endpoint(s) exposed: *.

Checked 1349 Java file(s), 8 config file(s) in 2268 ms. Found 6 finding(s), 4 medium, 2 high.

Line 80 is the only finding touching the actuator block above, and nothing in the run mentions
lines 83 or 85. The two properties that turn the wildcard from an information leak into a remote
kill switch and a memory dump are invisible to sprig.

The access gate is real and arrived in 3.4.0

Boot 3.4.0 introduced management.endpoint.<id>.access (none / read-only /
unrestricted) as a gate that sits after exposure.include, deprecating
management.endpoints.enabled-by-default in favour of management.endpoints.access.default.
From the metadata of spring-boot-actuator-autoconfigure:

Boot access family declared deprecation recorded
3.2.12 none none
3.3.13 none none
3.4.0 conditions.access, access.default, access.max-permitted enabled-by-defaultaccess.default
3.5.16 same three same

Evidence: measured against running apps

Every row below used spring-boot-starter-web + spring-boot-starter-actuator with
management.endpoints.web.exposure.include=*. Each row is its own JVM start. HPROF sizes vary
between runs, so the header is what proves the dump is real rather than the byte count.

Boot Extra config /actuator/env GET /actuator/heapdump POST /actuator/shutdown
2.3.12 none 200 200, JAVA PROFILE 1.0.2 404
2.3.12 ...shutdown.enabled=true 200
3.3.13 none 200 200, JAVA PROFILE 1.0.2 404
3.5.16 none 200 404 404
3.5.16 ...heapdump.access=unrestricted 200, JAVA PROFILE 1.0.2
3.5.16 ...shutdown.access=unrestricted 200, process exits
3.5.16 ...access=none on both 200 404 404

configprops, beans and mappings also return 200 under the wildcard alone on 3.3.13 and
3.5.16.

Three problems fall out of that table

1. The miss

On Boot 3.4+, access: unrestricted is the property that makes the endpoint reachable, and
sprig does not read it. A project with exposure.include: health,info and
management.endpoint.heapdump.access: unrestricted gets no finding at all while the heap dump
is served. halo sets both the wildcard and access, so sprig reports the less serious half of
its configuration and stays silent on the worse half.

2. The heapdump claim is version-dependent

docs/rules/SPR-CONFIG-001.md states that /actuator/heapdump "leaks process memory — including
tokens and keys". True through 3.3, false from 3.4 without access. The env half of the same
sentence holds on every version measured.

3. The bare shutdown token looks like a false positive

ActuatorExposureRule treats shutdown in exposure.include as a sensitive token worth a HIGH
finding. Exposure alone never reached that endpoint on any version measured: 404 on 2.3.12,
3.3.13 and 3.5.16 alike. It needs shutdown.enabled=true through 3.3, or
shutdown.access=unrestricted from 3.4, because the endpoint is disabled by default in every
release. Adding --management.endpoint.shutdown.enabled=true to the 2.3.12 probe turned the
same request from 404 into 200, which is the control for that claim.

exposure.include: shutdown on its own is therefore a HIGH finding with nothing behind it.

Suggested detection

  1. Read management.endpoint.<id>.access. unrestricted on a sensitive id (shutdown,
    heapdump, env, threaddump) is the signal, as is the blanket
    management.endpoints.access.default: unrestricted. For Boot 2.x through 3.3 the equivalent
    is management.endpoint.<id>.enabled: true. Note that management.endpoint.shutdown.access
    is absent from Spring's own configuration metadata, so [bug]: ConfigKeyMetadataTest treats absence from Spring's metadata as proof a property does not exist #44 has to land first.
  2. Split the SPR-CONFIG-001 doc by version, the way SPR-CONFIG-004's doc now does. The wildcard
    leaks env, configprops, beans and mappings across the whole supported range. It leaks
    heap dumps through 3.3, and from 3.4 only when access is also set.
  3. Drop the bare shutdown token, or pair it with the property that actually enables the
    endpoint.

Severity is worth deciding here rather than inheriting. The wildcard alone on Boot 3.4+ is an
information leak, not memory disclosure and not remote shutdown. Whether that is still HIGH, and
whether access: unrestricted on shutdown deserves CRITICAL, are different questions from the
one the rule answers today.

Environment

  • sprig version: sprig 0.1.0, at 80fd14e
  • Java version: openjdk version "17.0.20" 2026-07-21 (Homebrew, build 17.0.20+0)
  • Probes: Boot 2.3.12.RELEASE, 3.3.13 and 3.5.16, spring-boot-starter-web +
    spring-boot-starter-actuator, no actuator configuration beyond the columns shown
  • Metadata read from spring-boot-actuator-autoconfigure 3.2.12, 3.3.13, 3.4.0, 3.4.1, 3.5.16
  • Scan target: halo at a559d15, which builds on Spring Boot 4.1.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationREADME, rule docs, or other documentationfalse-negativeA real misconfiguration wasn't caughtfalse-positiveA rule flagged code that isn't actually vulnerable

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions