Skip to content

Validate requests by path info and keep runtime env out of the cache - #57

Merged
stixx merged 4 commits into
mainfrom
fix/validate-without-servers
Oct 3, 2026
Merged

stixx merged 4 commits into
mainfrom
fix/validate-without-servers

Conversation

@stixx

@stixx stixx commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

0.14.1 caches each Nelmio area's OpenAPI document (at cache:warmup or on first use) and validates requests against it. Values Nelmio resolves from %env()% were frozen into that cache at warm-up time. With the common container pattern (build one image, inject the real configuration at runtime) the document kept build-time values forever in production.

That matters for servers: league's validator matches server path + request path. A server URL set at runtime with a path, such as https://api.example.com/v1, therefore made valid requests fail with "no such operation".

Validate by path info, ignore servers:

  • RequestValidator validates the request's Symfony path info, its path within the application, which is exactly what the router matched. Nelmio's paths are those route paths.
  • OpenApiSpecCache leaves servers out of the cached document at root, path and operation level.
  • Result: env-based server URLs work without a rebuild, with no per-request cost and no dependency on Nelmio internals at runtime.
  • Also fixes an older bug: a server URL whose path differed from the application's base URL made every request fail, before 0.14.1 too.
  • Behaviour change: server path prefixes no longer restrict which requests are validated. The router has already matched the route by then, so only false rejections disappear.

Other runtime env values: env vars used by the rest of Nelmio's documentation, or by an area's security configuration (for example a security scheme's header name), are collected per area when the container is compiled. Their runtime values are part of the cache file name, so a value that differs from the one at warm-up generates its own document once.

  • They're read with default::, so an unset variable doesn't break the container.
  • Env vars inside servers (root, path and operation level) are excluded, since validation no longer uses them.
  • Read-only build directory: the runtime document is written to kernel.cache_dir instead, so it is still generated only once.
  • Runtime generation fails (for example an env var set at warm-up is unset at runtime): outside debug mode, validation uses the document cached at warm-up, keeps it for that runtime value and logs a warning once, instead of failing the request. In debug mode the error is thrown.

Also:

  • Warm-up warning: cache:warmup logs a warning on the stixx_openapi_command channel when it can't cache an area's document, for example because an env var Nelmio's configuration uses is unset. It used to skip the area silently.
  • Versioned file name (openapi.v2.…): documents cached by 0.14.1 aren't reused, which matters because production never regenerates them.
  • docs/validation.md: the 0.14.1 note about frozen runtime values is replaced with the new behaviour, plus advice to define Nelmio's env vars during cache:warmup.

Tests

  • Functional (RuntimeEnvironmentTest): warm with API_URL=http://localhost, then run with API_URL=https://api.example.com/v1.
    • An app mounted under /v1 and an app that isn't both validate (201). On the 0.14.1 code the first gets 400.
    • A document generated with server path /v1 validates requests to an app not mounted below it (201; 400 on the 0.14.1 code).
    • A different runtime API_VERSION (from documentation.info) generates one more document.
    • No cached document contains servers.
  • Unit:
    • servers are stripped at every level, and the env fingerprint is part of the file name;
    • an area that fails to warm is logged;
    • the validator uses the path info (a request under base URL /v1 validates against path /test; without the change, NoPath);
    • a read-only build directory caches the runtime document in the cache directory;
    • a runtime environment that can't be described falls back to the warmed document, not another cached one, keeps it for the next request, and doesn't fall back in debug mode;
    • a warm-up copy that can't be kept as the fallback is logged as such, while the area's document stays cached;
    • the compiler pass collects the env vars of documentation and security config, excluding servers but not a schema property named servers.
  • 300 tests pass; cs-fixer and phpstan (max) are clean; the changelog check passes.

Summary by CodeRabbit

  • Bug Fixes
    • Request validation now matches application paths to documented OpenAPI paths, including when the application is mounted under a URL prefix.
    • OpenAPI documents now reflect runtime environment values used in documentation and security settings. If runtime document generation fails outside debug mode, the warmed document is used when available, with a warning.
    • Cache warming now warns when an area’s document cannot be cached and can store documents in the cache directory when the build directory is read-only.
  • Documentation
    • Updated guidance on path matching, environment variables, document caching, and generation failures.

0.14.1 cached each area's OpenAPI document at warm-up, freezing %env()%
values from Nelmio's documentation: a server URL with a path set at runtime
made valid requests fail, because league matches the server path plus the
request path. Validate the request's Symfony path info against the
document's paths and leave servers out of the cached document at every
level; that also fixes a server path that differs from the base URL.

Key the cached document on the runtime values of the other env vars in the
area's documentation and security config, collected at compile time, so a
different value generates its own document once. Log an area the warmer
could not cache, and version the file name so 0.14.1 caches are not reused.
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: stixx/openapi-command-bundle/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ac9e2746-9cf2-4a42-b9f5-33127030a4d3
📥 Commits

Reviewing files that changed from the base of the PR and between 2e9f071 and 229c623.

⛔ Files ignored due to path filters (1)
  • tests/Mock/Validator/PathDescriber.php is excluded by !tests/Mock/**
📒 Files selected for processing (12)
  • CHANGELOG.md
  • config/validators.php
  • docs/validation.md
  • src/DependencyInjection/Compiler/CollectNelmioApiDocRoutesPass.php
  • src/Validator/OpenApiSpecCache.php
  • src/Validator/RequestValidator.php
  • tests/Functional/ApiRequestTest.php
  • tests/Functional/Resources/config/env_documentation.php
  • tests/Functional/RuntimeEnvironmentTest.php
  • tests/Unit/DependencyInjection/Compiler/CollectNelmioApiDocRoutesPassTest.php
  • tests/Unit/Validator/OpenApiSpecCacheTest.php
  • tests/Unit/Validator/RequestValidatorTest.php

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The change collects Nelmio environment values for each area and uses them to identify cached OpenAPI documents. It adds warm-up logging and runtime fallback behavior, removes server declarations from generated documents, and validates requests using Symfony path info.

Changes

OpenAPI validation and caching

Layer / File(s) Summary
Collect per-area documentation environment values
src/DependencyInjection/Compiler/CollectNelmioApiDocRoutesPass.php, config/validators.php, tests/Unit/DependencyInjection/Compiler/CollectNelmioApiDocRoutesPassTest.php
The compiler pass collects environment placeholders from documentation and security configuration, excluding server declarations. The cache service receives the collected values and a logger.
Generate and cache environment-specific documents
src/Validator/OpenApiSpecCache.php, tests/Unit/Validator/OpenApiSpecCacheTest.php, tests/Functional/ApiRequestTest.php, tests/Functional/Resources/config/env_documentation.php, tests/Functional/RuntimeEnvironmentTest.php, docs/validation.md, CHANGELOG.md
Cache filenames include an environment fingerprint. The cache checks build and cache directories, logs warm-up failures, and can use a warmed document after runtime generation fails outside debug mode. Tests and documentation cover these behaviors.
Match application paths without server declarations
src/Validator/RequestValidator.php, src/Validator/OpenApiSpecCache.php, tests/Unit/Validator/RequestValidatorTest.php, tests/Unit/Validator/OpenApiSpecCacheTest.php, tests/Functional/RuntimeEnvironmentTest.php
Request validation uses Symfony path info. Generated OpenAPI documents omit server declarations. Tests cover application-relative paths and runtime server values.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant OpenApiSpecCache
  participant CacheDirectory
  participant NelmioGenerator
  participant LoggerInterface
  OpenApiSpecCache->>CacheDirectory: Look up environment-specific document
  OpenApiSpecCache->>NelmioGenerator: Generate document on cache miss
  NelmioGenerator-->>OpenApiSpecCache: Return generated JSON
  OpenApiSpecCache->>CacheDirectory: Write generated document
  NelmioGenerator-->>OpenApiSpecCache: Return generation error
  OpenApiSpecCache->>LoggerInterface: Log warning
  OpenApiSpecCache->>CacheDirectory: Read warmed fallback outside debug mode
Loading

Merge Risk: ⚪ Minimal · up to 229c6

No actionable issue has been established that would prevent merging after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 229c6

Normal operation separates cached validation documents by runtime configuration, while application routing remains in control of endpoint reachability. Generation failures deliberately reuse and persist the warm-up document, so runtime configuration may not take effect in that degraded state. No new authorization bypass was established, but recovery and concurrent-write guarantees remain partly unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A persisted degraded document can affect all validation requests for its area and runtime identity, including later processes sharing that cache. The shown loader does not derive environment identity or storage paths from request input; fallback requires a cache miss, generation failure, and an existing warmed document. Production tenant and shared-storage boundaries were not established.

Trust Boundaries and Controls

  • observed — Validation is gated to main requests classified into a Nelmio area. Area selection uses the original Symfony request's route mapping, with a path fallback for unmatched routes. The changed validator alters only the downstream PSR URI path; it does not modify the original route attributes, method, or host or dispatch a controller.

Resilience and Maintainability Implications

  • observed — The degraded-mode policy is explicit: retain the warm-up document for the failing runtime value and log a warning once. Tests confirm persistence and refusal in debug mode. Whether warm-up security metadata is acceptable for a particular production deployment remains an application policy question.

Hardening Proposals

  • proposed — For applications where environment-dependent metadata participates in security enforcement, consider requiring compatible security-input provenance before accepting a warmed fallback, or keeping degraded documents distinguishable and retryable rather than permanently labeling them as current-runtime documents.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.64% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 10 files. (2 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies both request path-info validation and runtime-environment caching. The phrase “keep runtime env out of the cache” is imprecise because the change uses runtime environment values i…
Full details: Docstring Coverage

Explanation

Docstring coverage is 22.64% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 10 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

stixx added 3 commits October 3, 2026 15:21
…only, and fall back to the warmed one

Also leave env vars of nested servers out of the cache key.
Strip servers only where the cached document does.
@stixx
stixx merged commit 2c2b75c into main Oct 3, 2026
10 checks passed
@stixx
stixx deleted the fix/validate-without-servers branch October 3, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant