Skip to content

[bug]: ConfigKeyMetadataTest treats absence from Spring's metadata as proof a property does not exist #44

Description

@vianbas

Related: #42, which adds the test. This is not an argument against that test, which caught
six invented keys across three rules. It is about what its failure message claims. #45 is
the concrete rule this currently blocks.

Summary

ConfigKeyMetadataTest fails any rule key missing from the vendored index of Spring Boot's
configuration metadata, and tells the reader those rules "cannot fire on a real project and any
finding they produce is wrong."

That inference only holds in one direction. Spring's metadata is authoritative for presence:
a declared key is a real property. It is not authoritative for absence. Boot ships properties
it never declares, and the test currently reports those as invented.

The counterexample is management.endpoint.shutdown.access, which halo sets in its production
configuration and which Boot acts on.

Evidence 1: the key is absent from the index

Replaying the test's own isDeclared() against
src/test/resources/spring-metadata/spring-property-index.txt (2920 properties, Boot 2.0.9
through 3.5.16, three artifacts):

isDeclared(management.endpoint.shutdown.access) = False
isDeclared(management.endpoint.heapdump.access) = False
isDeclared(management.endpoints.access.default) = True
Map prefixes that are ancestors of management.endpoint.shutdown.access: []

No Map prefix rescues it, so the test rejects the key outright.

Evidence 2: Boot does not declare it either

The index is not stale or mis-parsed. spring-boot-actuator-autoconfigure 3.5.16 declares 612
properties, of which the entire access family is:

management.endpoint.conditions.access
management.endpoints.access.default
management.endpoints.access.max-permitted

  management.endpoint.shutdown.access: ABSENT
  management.endpoint.heapdump.access: ABSENT
  any name containing 'heapdump': []

This is not new in 3.4. The property access replaced has the same gap.
management.endpoint.shutdown.enabled is absent from the index too, and on Boot 2.3.12 adding
--management.endpoint.shutdown.enabled=true turned POST /actuator/shutdown from 404 into 200.
Per-endpoint configuration has never been fully declared, in either generation of the property.

Evidence 3: runtime behaviour

Boot 3.5.16, spring-boot-starter-web + spring-boot-starter-actuator, with
management.endpoints.web.exposure.include=* in every run. Each row is a separate JVM start,
because a successful POST /actuator/shutdown ends the process and would poison any later
request in the same run:

Extra config Request Result
none POST /actuator/shutdown 404, app alive
management.endpoint.shutdown.access=none POST /actuator/shutdown 404, app alive
management.endpoint.shutdown.access=unrestricted POST /actuator/shutdown 200 {"message":"Shutting down, bye..."}, process exits
none GET /actuator/heapdump 404
management.endpoint.heapdump.access=none GET /actuator/heapdump 404
management.endpoint.heapdump.access=unrestricted GET /actuator/heapdump 200, ~31 MB, header JAVA PROFILE 1.0.2

Zero unknown-property warnings were logged in any run. That is the same silence that hid the
defects in #39 and #40. Here it means the opposite: the property is real and Boot is acting on it.

Impact

A rule keyed on management.endpoint.shutdown.access is correct and this test rejects it. That
is not hypothetical, it is #45.

The failure text also teaches the wrong lesson to the next contributor. "This key is wrong" and
"I could not confirm this key" call for different responses, and only the second one is
supported by the evidence the test has.

What #39 and #40 actually rested on

Worth being precise, because the test's wording currently takes credit it has not earned.
spring.web.cors.* and spring.security.debug were not condemned by metadata absence alone.
Each was confirmed by runtime observation and by searching the relevant jars for a binding that
never existed. Metadata absence is what pointed at them. It was not the proof.

Proposed fix

Keep the test failing on absence. It is the right default and it earned its place. Fix what the
failure claims:

  1. ConfigKeyMetadataTest javadoc and the everyRuleKeyExistsInSpring failure message: absence
    means unconfirmed, not wrong. It is a prompt to verify against the Boot source or a
    running app before shipping the key, not a verdict.
  2. Same correction to the "Property keys" bullet in CONTRIBUTING.md, which currently says a
    rule keyed on an undeclared property "can never fire".
  3. Document what an ACCEPTED_ABSENCES entry requires: a runtime observation, or a pointer to
    the Boot source that binds the property. Without that bar the map is just a mute button, and
    the next contributor to hit a red test will reach for it.

Reproducing

$ python3 - <<'EOF'
I = "src/test/resources/spring-metadata/spring-property-index.txt"
declared, maps = set(), set()
for line in open(I, encoding="utf-8"):
    n = line.strip()
    if not n or n.startswith("#"):
        continue
    if n.endswith("*"):
        n = n[:-1]
        maps.add(n)
    declared.add(n)
def is_declared(k):
    return k in declared or any(k.startswith(p + ".") for p in maps)
print(is_declared("management.endpoint.shutdown.access"))   # False
EOF

$ java -jar app.jar --management.endpoints.web.exposure.include='*' \
      --management.endpoint.shutdown.access=unrestricted
$ curl -X POST localhost:8080/actuator/shutdown
{"message":"Shutting down, bye..."}

Environment

  • sprig version: sprig 0.1.0, at 80fd14e (the fix: repoint config rules at property keys Spring actually reads #42 branch)
  • Java version: openjdk version "17.0.20" 2026-07-21 (Homebrew, build 17.0.20+0)
  • Probes: Boot 3.5.16, spring-boot-starter-web + spring-boot-starter-actuator
  • Metadata read from spring-boot-actuator-autoconfigure 3.2.12, 3.3.13, 3.4.0, 3.4.1, 3.5.16

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

    bugCrash, wrong exit code, or other non-detection defectdocumentationREADME, rule docs, or other documentation

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions