Skip to content

feat(gateway): add anchor type/access axes and asset-serving action#84

Merged
OliverWolffGIP merged 20 commits into
mainfrom
feature/anchor-types-assets
Jul 21, 2026
Merged

feat(gateway): add anchor type/access axes and asset-serving action#84
OliverWolffGIP merged 20 commits into
mainfrom
feature/anchor-types-assets

Conversation

@OliverWolffGIP

@OliverWolffGIP OliverWolffGIP commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds two explicit type (proxy | bff | asset) and access (public | authenticated) axes to every gateway.yaml anchor — replacing passthrough with proxy + access: public as a clean-slate breaking rename — and introduces asset serving as a new terminal action with two sources (directory and upstream). Fail-closed boot validation covers the full access/auth matrix, wired through the startup-event path so validation cannot be bypassed by CDI-proxy laziness. Asset responses go through a gateway-owned envelope (fixed content-type map, nosniff, governed caching, stripped Set-Cookie, GET/HEAD only), with canonicalize-and-confine path safety applied to both sources before the source is touched. The path-traversal threat-model entry is reopened and closed with a generator-driven traversal/encoding regression corpus. The change also bootstraps the doc/user/ and doc/development/ documentation tree and records the design rationale in two new ADRs.

Changes

  • Anchor type/access axesAnchorType, AccessLevel, AnchorConfig, RouteConfig, RouteTableBuilder, ConfigValidator, ConfigModelReflection, and the gateway.schema.json / endpoint.schema.json schemas now require and enforce type + access on every anchor; passthrough is renamed to proxy + access: public.
  • Fail-closed boot validationConfigValidator enforces the access→auth matrix (access: authenticated requires a fully-backed auth posture; access: public + auth block fails boot; type: bff requires access: authenticated), asserted via the startup-event path in ConfigValidatorTest / ConfigProducerTest (not a direct-call test), per the PR feat(benchmarks): replace wrk with k6, add APISIX comparative baseline #82 lazy-CDI-proxy lesson.
  • Asset serving terminal action — new de.cuioss.sheriff.api.asset package: AssetSource, DirectoryAssetSource, UpstreamAssetSource, PathConfinement, AssetResponseEnvelope, plus AssetConfig / ResolvedAsset config-model support and RouteRuntime / GatewayEdgeRoute / DispatchStage / RouteRuntimeAssembler wiring. UpstreamAssetSource reuses the existing upstream client, circuit breaker, and SSRF controls (scheme allowlist, EgressPolicy, followRedirects(NEVER), bounded size/timeout) rather than a parallel fetch stack.
  • Path safetyPathConfinement applies canonicalize-and-confine to both sources before the source is touched, with auth-before-source-resolution ordering enforced in the edge pipeline.
  • Threat modeldoc/security-threat-model.adoc path-traversal entry reopened and closed against the canonicalize-then-contain design and the new adversarial traversal corpus (DirectoryAssetSourceTest, UpstreamAssetSourceTest, PathConfinementTest).
  • ADRs and docsdoc/adr/0013-anchor-type-access-axes.adoc, doc/adr/0014-asset-serving-terminal-action.adoc, plus updates to doc/configuration.adoc and doc/architecture.adoc.
  • Documentation-tree bootstrap — new doc/user/README.adoc and doc/development/README.adoc, a fifth Conventions bullet in doc/plan/README.adoc for the three-layer documentation convention, and a doc/README.adoc classification fix for configuration.adoc's dual reference/design role.
  • Integration testsDirectoryAssetServingIT and UpstreamAssetServingIT against a real mounted directory and a real secondary origin server, with supporting integration-tests docker-compose / sheriff-config fixtures (assets.yaml, assets-secure.yaml, asset-origin content).

Test Plan

  • Verification command passed (python3 .plan/execute-script.py plan-marshall:build-maven:maven run --command-args "verify -Ppre-commit")
  • Full verify passed (python3 .plan/execute-script.py plan-marshall:build-maven:maven run --command-args "verify")
  • Manual testing completed (if applicable)

Related Issues

None.


Generated by plan-finalize skill

Summary by CodeRabbit

  • New Features

    • Added an asset terminal action for routes, supporting directory and upstream sources with confined paths, size limits, SSRF protections, and auth-before-resolution behavior.
    • Asset responses are gateway-governed: fixed content-type mapping, X-Content-Type-Options: nosniff, Set-Cookie stripping, and Cache-Control: no-store for authenticated access.
    • Only GET/HEAD are allowed; upstream redirects and upstream failures are handled safely.
  • Documentation

    • Added and updated configuration, architecture, security, operator, contributor docs, and ADRs for asset serving and anchor type/access fail-closed validation.
  • Tests

    • Added unit and integration tests for directory and upstream assets, header governance, method enforcement, and configuration validation.

OliverWolffGIP and others added 15 commits July 20, 2026 21:55
Introduce the operator-facing (doc/user/) and contributor-facing
(doc/development/) documentation layers as tree-index documents,
distinct from the design docs under doc/. Add the three-layer
documentation convention as a fifth bullet to the plan README's
Conventions section, and state configuration.adoc's dual
reference/design role in the design-documents index without
relocating the file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Add doc/plan/10-anchor-types-assets.adoc, the design spec covering the
nine deliverable groups -- the required type/access anchor axes and the
passthrough->proxy rename, the fail-closed access->auth boot matrix, the
asset terminal action with directory and upstream sources, the
gateway-owned response envelope, canonicalize-and-confine path safety,
auth-before-source-resolution ordering, the threat-model reopening, the
integration tests, and the reference-doc updates. Register plan 10 in the
plan-sequence table and the implementation-plans narrative.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Add ADR-0013 recording the two required, orthogonal anchor
classification axes (type/access) with the three-argument rationale
(non-OAuth extensibility, fail-closed declaration, linear growth) and
the infrastructure-proxy vs application-security prior-art survey; it
extends ADR-0007 and covers the passthrough->proxy value rename. Add
ADR-0014 recording asset serving as a second terminal action with the
two-source model (directory + upstream reusing the ADR-0008
SSRF-controlled data plane, no parallel fetch stack), the gateway-owned
response envelope, and canonicalize-and-confine with
auth-before-source-resolution ordering. Register both in the
Architecture Decisions table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Introduce the AnchorType (proxy/bff/asset) and AccessLevel
(public/authenticated) enums and make both a required, non-null
component of AnchorConfig, enforced in the canonical constructor. Add
the two axes as required properties on every anchor in
gateway.schema.json (case-insensitive lowercase YAML binds to the
uppercase constants), and register the new enums for native reflection.
The passthrough->proxy rename is an anchor-type value rename only;
tls.passthrough_sni is untouched.

Sweep every existing AnchorConfig construction site: the model contract,
loader, route-table-builder, and validator tests now supply type and
access, and the anchored / anchor-squatter fixtures carry the required
axes and stay access->auth matrix consistent (the bff anchor gains a
bearer floor; the squatter anchor is access: public). Adds binding and
mandatory-field coverage for the two new axes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Add the ADR-0013 access->auth matrix rules to ConfigValidator: a type
'bff' anchor must be access: authenticated; an access: authenticated
anchor requires a non-'none' auth floor with its backing block present
(bearer needs token_validation issuers, session needs oidc); an access:
public anchor must not declare an auth block. Every violation collects
into the shared errors list and never fails fast.

Cover each matrix rule in ConfigValidatorTest (generator/enum-driven over
the enumerable posture space) plus the single-pass aggregation contract,
and keep the ADR-0007 anchor fixtures matrix-consistent. Prove enforcement
on the eager startup-event path in ConfigProducerTest: a matrix-violating
anchor aborts boot through onStartup -> buildOnce, not merely a direct
validator call.

Co-Authored-By: Claude <noreply@anthropic.com>
…finement

Introduce the asset terminal action (ADR-0014). Add AssetConfig (source
discriminator directory|upstream plus source-specific fields) and wire an
optional, mutually-exclusive-with-upstream asset component into RouteConfig
and the endpoint schema.

Create the framework-agnostic asset package: PathConfinement canonicalizes an
untrusted sub-path through the cui-http url-path pipeline and confines it under
a configured root, rejecting the whole traversal/encoding attack class before
any source is touched; AssetResponseEnvelope is the gateway-owned response
governance (fixed content-type map, nosniff, forced no-store for authenticated
access, stripped Set-Cookie, GET/HEAD-only); AssetSource is the sealed source
seam carrying the auth-before-source-resolution ordering contract, permitting
the directory and upstream sources completed in later deliverables.

Cover confinement with an explicit, exhaustive adversarial traversal/encoding
corpus proving no vector escapes the root, and assert every envelope governance
rule plus the sealed seam.

Co-Authored-By: Claude <noreply@anthropic.com>
Implement DirectoryAssetSource: serve regular files from a configured
directory root through the shared PathConfinement and AssetResponseEnvelope,
with GET/HEAD-only serving, a bounded file-size cap, and a governed Served
response. Confinement is applied before the filesystem is touched, so an
out-of-root escape reads no byte.

Cover in-root serving with the governed envelope, out-of-root denial,
content-type resolution from the fixed map, forced no-store for authenticated
access, HEAD-without-body, and write-verb rejection.

Co-Authored-By: Claude <noreply@anthropic.com>
…envelope

Implement UpstreamAssetSource: serve from a fixed-topology upstream reusing the
resolved-upstream model and the shared PathConfinement and AssetResponseEnvelope.
The untrusted remainder is confined before the upstream is touched; the fetch
runs through an injectable seam whose default embodies the SSRF controls — a
scheme allowlist, followRedirects(NEVER), a connect/read timeout, and a bounded
body — so no parallel async fetch stack is introduced. Every response is governed
by the gateway: the content type is taken from the fixed map (overriding a hostile
upstream Content-Type), Set-Cookie is stripped, and an authenticated asset is
forced to no-store even when the upstream sent Cache-Control: public.

Cover the three hostile-response overrides, confinement-before-fetch, scheme
allowlist, no-redirect-follow, bounded size, and timeout mapping through the
deterministic fetch seam.

Co-Authored-By: Claude <noreply@anthropic.com>
Reopen GW-11 to account for the new asset-serving file-path surface (ADR-0014):
document that the traversal class is closed for both the local-directory and
upstream sources via canonicalize-then-contain (cui-http URLPathValidationPipeline
+ the shared gateway PathConfinement) proven by the generator-driven
traversal/encoding regression corpus, and add an upstream-asset-source clause to
GW-05 noting the reuse of the fixed-topology upstream, scheme allowlist,
EgressPolicy posture, and followRedirects(NEVER). Update the GW-05 and GW-11
validation-matrix rows and statuses accordingly.

Co-Authored-By: Claude <noreply@anthropic.com>
Apply the idempotent OpenRewrite/format normalizations the pre-commit quality
gate produced over the asset package (null-safe equalsIgnoreCase ordering and
import formatting). Behaviour is unchanged; the full pre-commit gate is green.

Co-Authored-By: Claude <noreply@anthropic.com>
ResolvedRoute now carries a terminal-action union (upstream XOR asset) via a new ResolvedAsset model; RouteTableBuilder materializes directory/upstream asset actions, RouteRuntimeAssembler/DispatchStage/GatewayEdgeRoute dispatch type: asset routes through DirectoryAssetSource/UpstreamAssetSource behind PathConfinement and AssetResponseEnvelope with auth-before-source ordering, and ConfigValidator enforces the type/terminal-action consistency rules (ADR-0013/0014).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
…ary origin

Adds a secondary asset-origin static server and a mounted /app/assets volume to the compose stack; the IT gateway config gains type/access axes on all anchors plus two asset anchors (public directory+upstream, authenticated directory), with require:none relocated off the now-matrix-constrained public anchors onto the httpbin endpoint. DirectoryAssetServingIT and UpstreamAssetServingIT exercise serving, GET/HEAD-only, envelope governance, and auth-before-source ordering end-to-end (ADR-0013/0014).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
…minal action

configuration.adoc gains the mandatory type/access anchor axes, the access->auth matrix rules, the passthrough->proxy value rename, and the asset terminal action with both sources and its envelope/path-safety governance; architecture.adoc documents asset serving as the second terminal action, the auth-before-source-resolution ordering, the shared gateway-owned response envelope, and the upstream source's reuse of the existing data plane (ADR-0013/0014).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Collapse the three near-identical served-response records
(DirectoryAssetSource.Served, UpstreamAssetSource.Served,
DispatchStage.AssetDispatch) into a single AssetSource.Served record on
the sealed AssetSource interface, and reduce DispatchStage.serveAsset to
a thin validating delegate over source.serve(...).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Key asset cache governance on the route's effective auth posture instead
of the anchor's static access axis (an auth-strengthened route under a
public anchor now serves no-store), close the directory-source symlink
escape by verifying toRealPath() containment under the configured root
(fail-closed on I/O error), and replace the upstream asset source's
buffer-then-truncate body handler with a streaming subscriber that
aborts the fetch the instant maxBytes is crossed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @OliverWolffGIP, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds required anchor type/access axes and fail-closed access/auth validation, introduces directory and upstream asset terminal actions, governs asset responses, confines paths, integrates asset dispatch, and adds unit, startup, documentation, and integration coverage.

Changes

Asset terminal serving

Layer / File(s) Summary
Asset and anchor contracts
api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/*, api-sheriff/src/main/resources/schema/*
Adds anchor classification fields, asset configuration models, mutually exclusive terminal actions, schemas, reflection registration, and defensive response shapes.
Configuration validation and resolution
api-sheriff/src/main/java/de/cuioss/sheriff/api/config/*, api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/ConfigProducer.java
Validates access/auth combinations and asset source requirements, resolves directory or topology-backed upstream assets, and includes asset aliases in topology resolution.
Confined asset sources and response governance
api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/*
Adds confined directory serving, bounded non-redirecting upstream fetching, fixed content types, header governance, method restrictions, and defensive response records.
Runtime assembly and edge dispatch
api-sheriff/src/main/java/de/cuioss/sheriff/api/edge/*, api-sheriff/src/main/java/de/cuioss/sheriff/api/routing/RouteRuntime.java
Adds asset-source runtime wiring, bypasses proxy dispatch for assets, and writes buffered governed responses.
Behavior, startup, documentation, and integration coverage
api-sheriff/src/test/*, integration-tests/*, doc/*
Tests confinement, headers, source behavior, terminal resolution, access/auth validation, startup behavior, and directory/upstream asset requests while updating fixtures and design documentation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • cuioss/API-Sheriff#60: Extends the configuration and route-resolution pipeline used by asset terminal materialization.
  • cuioss/API-Sheriff#74: Relates to effective authentication posture resolution used by asset access governance.
  • cuioss/API-Sheriff#76: Modifies the edge dispatch and runtime assembly components extended with asset serving.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: new anchor type/access axes and the asset-serving terminal action.

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the asset-serving terminal action (ADR-0014) and anchor type/access classification axes (ADR-0013), allowing the gateway to serve static content directly from local directories or secondary origins under strict security controls. Feedback on the changes focuses on critical performance optimizations: offloading blocking asset-serving operations from the Vert.x event loop to a worker thread, caching the resolved real path of the root directory in DirectoryAssetSource to prevent redundant disk I/O, and adding a fast-return check in CappedByteArrayBodySubscriber.onNext to avoid processing overhead after subscription cancellation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
integration-tests/src/test/java/de/cuioss/sheriff/api/integration/DirectoryAssetServingIT.java (1)

104-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Defensively handle a potentially null contentType.

If the server drops the response early or fails to return a Content-Type header (e.g., a bare 401 without a body during a test failure), response.contentType() will evaluate to null. This will cause a NullPointerException at .indexOf(';') and hide the root test failure. Returning a fallback for null allows the assertEquals caller to fail cleanly with a descriptive message.

💡 Proposed refactor
     private static String problemType(String contentType) {
+        if (contentType == null) {
+            return "";
+        }
         int semicolon = contentType.indexOf(';');
         return (semicolon < 0 ? contentType : contentType.substring(0, semicolon)).trim();
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@integration-tests/src/test/java/de/cuioss/sheriff/api/integration/DirectoryAssetServingIT.java`
around lines 104 - 107, Update problemType to handle a null contentType before
calling indexOf; return a suitable fallback string for null so the assertEquals
caller reports the original test failure instead of throwing a
NullPointerException, while preserving the existing parsing and trimming
behavior for non-null values.
api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/UpstreamAssetSource.java (1)

141-176: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Optional: HEAD triggers a full upstream body download. The UpstreamFetcher always issues GET, so a HEAD request fetches (up to maxBytes of) the upstream body only to discard it (serveBody is false for HEAD). For large assets this is wasted bandwidth/latency. Additionally, fetched.body() clones the (potentially 10 MiB) array on both Line 168 and Line 174; binding it to a local once avoids the redundant copy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/UpstreamAssetSource.java`
around lines 141 - 176, Update the upstream fetch flow in serve and the
UpstreamFetcher API so HEAD requests use an upstream HEAD request and do not
download a response body, while GET retains the existing body behavior. Bind
fetched.body() to a local byte array once before the size check and response
construction, reusing it to avoid repeated accessor calls and redundant copying.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceTest.java`:
- Around line 236-248: Update the `@DisplayName` on shouldBuildResolvableUri to
describe successful URI construction and upstream fetching for a resolvable
remainder, matching the assertions and fetcher behavior in the test body.

---

Nitpick comments:
In
`@api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/UpstreamAssetSource.java`:
- Around line 141-176: Update the upstream fetch flow in serve and the
UpstreamFetcher API so HEAD requests use an upstream HEAD request and do not
download a response body, while GET retains the existing body behavior. Bind
fetched.body() to a local byte array once before the size check and response
construction, reusing it to avoid repeated accessor calls and redundant copying.

In
`@integration-tests/src/test/java/de/cuioss/sheriff/api/integration/DirectoryAssetServingIT.java`:
- Around line 104-107: Update problemType to handle a null contentType before
calling indexOf; return a suitable fallback string for null so the assertEquals
caller reports the original test failure instead of throwing a
NullPointerException, while preserving the existing parsing and trimming
behavior for non-null values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d1babec-27f2-4b4a-8213-6db3d66a11d9

📥 Commits

Reviewing files that changed from the base of the PR and between fbf84e3 and 1515b00.

⛔ Files ignored due to path filters (1)
  • integration-tests/src/main/docker/asset-origin/logo.svg is excluded by !**/*.svg
📒 Files selected for processing (56)
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/AssetResponseEnvelope.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/AssetSource.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/DirectoryAssetSource.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/PathConfinement.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/UpstreamAssetSource.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/package-info.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/RouteTableBuilder.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/AccessLevel.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/AnchorConfig.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/AnchorType.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/AssetConfig.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/ResolvedAsset.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/ResolvedRoute.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/model/RouteConfig.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/validation/ConfigValidator.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/edge/DispatchStage.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/edge/GatewayEdgeRoute.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/edge/RouteRuntimeAssembler.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/ConfigModelReflection.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/routing/RouteRuntime.java
  • api-sheriff/src/main/resources/schema/endpoint.schema.json
  • api-sheriff/src/main/resources/schema/gateway.schema.json
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/AssetResponseEnvelopeTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/DirectoryAssetSourceTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/PathConfinementTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/config/RouteTableBuilderTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/config/load/ConfigLoaderTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/config/model/ConfigModelContractTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/config/validation/ConfigValidatorTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/edge/DispatchStageTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/edge/GatewayEdgePipelineTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/edge/GatewayEdgeRouteTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/edge/RouteRuntimeAssemblerTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/quarkus/ConfigProducerTest.java
  • api-sheriff/src/test/resources/config/anchor-squatter/gateway.yaml
  • api-sheriff/src/test/resources/config/anchored/gateway.yaml
  • doc/README.adoc
  • doc/adr/0013-anchor-type-access-axes.adoc
  • doc/adr/0014-asset-serving-terminal-action.adoc
  • doc/architecture.adoc
  • doc/configuration.adoc
  • doc/development/README.adoc
  • doc/plan/10-anchor-types-assets.adoc
  • doc/plan/README.adoc
  • doc/security-threat-model.adoc
  • doc/user/README.adoc
  • integration-tests/docker-compose.yml
  • integration-tests/src/main/docker/assets/app.css
  • integration-tests/src/main/docker/sheriff-config/endpoints/assets-secure.yaml
  • integration-tests/src/main/docker/sheriff-config/endpoints/assets.yaml
  • integration-tests/src/main/docker/sheriff-config/endpoints/httpbin.yaml
  • integration-tests/src/main/docker/sheriff-config/gateway.yaml
  • integration-tests/src/main/docker/sheriff-config/topology.properties
  • integration-tests/src/test/java/de/cuioss/sheriff/api/integration/DirectoryAssetServingIT.java
  • integration-tests/src/test/java/de/cuioss/sheriff/api/integration/UpstreamAssetServingIT.java

OliverWolffGIP and others added 4 commits July 21, 2026 02:35
The native binary refused to start (ApiSheriff-200 binding failure for
EndpointConfig at /routes/0/asset) because the new AssetConfig record
and its nested Source enum were absent from ConfigModelReflection's
@RegisterForReflection targets. JVM-mode tests need no registration,
which is why every local gate was green while the CI native IT failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
ConfigProducer passed only the tls.passthrough_sni values as additional
topology aliases, so a source: upstream asset route's alias (e.g.
ASSET_ORIGIN) never reached TopologyResolver and boot refused to start
with ApiSheriff-200 "does not resolve in the topology" — the CI-native
failure at PR #84. Collect every enabled endpoint's upstream asset-route
alias into the additional-alias union.

Add the missing boot-path coverage: a positive ConfigProducerTest fixture
proving an upstream asset alias declared in topology.properties resolves
through the startup event, and a negative fixture pinning the exact
ApiSheriff-200 rejection when the alias is undeclared.

Confirmed by a clean native rebuild + full Docker IT run (both asset ITs
green).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
…ture

The verify-invalid-config-fails.sh Case 2 fixture predates the mandatory
anchor axes: its api/apiv1 anchors carried only path_prefix + auth, so
the config now dies at the schema layer (missing type/access) before the
ADR-0007 pairwise-disjoint rule runs, and the asserted marker never
appears. Give the fixture anchors type: proxy + access: public (dropping
the auth block the ADR-0013 matrix forbids on a public anchor) so the
sole remaining violation is the pairwise-disjoint overlap the negative
test exists to assert. Locally the script reuses an existing
api-sheriff:distroless tag, which is why a stale image masked this.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
Address the accepted review findings from the unified triage: fast-return
in the capped body subscriber's onNext once the cap has tripped (bounds
over-cap buffering during cancel propagation), correct a copied
@DisplayName on the resolvable-URI upstream test, and null-guard the
problemType extraction in the directory-asset IT for cleaner
negative-path diagnostics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL
@OliverWolffGIP
OliverWolffGIP enabled auto-merge July 21, 2026 05:12
Fix the three MAJOR reliability findings: content-aware equals/hashCode
and a length-only toString for the byte[]-bearing AssetSource.Served and
UpstreamFetcher.Fetched records (S6218, body bytes never rendered), and
a provably-guarded Optional access in ConfigValidator's anchor
consistency rule (S3655).

Close the new-code coverage gap (77.2% -> ~85%): exercise the real
SSRF-guarded transport fetcher and the streaming size-cap subscriber
against an in-process MockWebServer (header mapping, mid-flight abort at
maxBytes, read-timeout mapping), add record equals/hashCode/toString
contract tests, and cover the transport-IOException-to-502 path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HkVXPmpTBDyHu3L7eHrBiL

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceTest.java (1)

267-267: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Swap arguments to properly invoke Fetched.equals().

In JUnit 5, assertNotEquals(unexpected, actual) evaluates unexpected.equals(actual). By passing BODY (a byte[]) as the first argument, the assertion executes byte[].equals(first), which relies on array reference equality and trivially returns false. This means the unrelated-type check inside Fetched.equals(Object) is never actually tested.

To ensure Fetched.equals() is invoked and the branch is covered, swap the arguments.

♻️ Proposed fix
-                () -> assertNotEquals(BODY, first, "a Fetched never equals an unrelated type"));
+                () -> assertNotEquals(first, BODY, "a Fetched never equals an unrelated type"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceTest.java`
at line 267, Swap the arguments in the assertNotEquals call within
UpstreamAssetSourceTest so the Fetched instance first is passed as the
unexpected argument and BODY is passed as actual, ensuring
Fetched.equals(Object) handles the unrelated type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceTest.java`:
- Line 267: Swap the arguments in the assertNotEquals call within
UpstreamAssetSourceTest so the Fetched instance first is passed as the
unexpected argument and BODY is passed as actual, ensuring
Fetched.equals(Object) handles the unrelated type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e186244-0fad-4e06-8bca-638ef2b8d19a

📥 Commits

Reviewing files that changed from the base of the PR and between 2e5213a and e8be1ab.

📒 Files selected for processing (7)
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/AssetSource.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/UpstreamAssetSource.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/validation/ConfigValidator.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/AssetSourceServedTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceHttpFetcherTest.java
  • api-sheriff/src/test/java/de/cuioss/sheriff/api/asset/UpstreamAssetSourceTest.java
  • integration-tests/src/test/java/de/cuioss/sheriff/api/integration/DirectoryAssetServingIT.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/AssetSource.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/config/validation/ConfigValidator.java
  • integration-tests/src/test/java/de/cuioss/sheriff/api/integration/DirectoryAssetServingIT.java
  • api-sheriff/src/main/java/de/cuioss/sheriff/api/asset/UpstreamAssetSource.java

@OliverWolffGIP
OliverWolffGIP added this pull request to the merge queue Jul 21, 2026
Merged via the queue into main with commit 0f7f957 Jul 21, 2026
25 checks passed
@OliverWolffGIP
OliverWolffGIP deleted the feature/anchor-types-assets branch July 21, 2026 06:12
OliverWolffGIP added a commit that referenced this pull request Jul 21, 2026
Auditability layer for the protocol-processors work, authored doc-first:

- doc/adr/0015: WebSocket Origin fail-closed exact-match allowlist decision
  (GW-09/CSWSH rationale, no "any origin" default, why a new field over CORS,
  rejected alternatives).
- doc/adr/0016: gRPC trailers-only rejection status-mapping decision (why a
  gRPC client cannot consume problem+json, canonical mapping, h2 failure ->
  UNAVAILABLE, rejected alternatives).
- doc/plan/05-protocol-processors.adoc: orchestration notes (k6 supersedes the
  ghz/k6 split per Clarification 1, config model final via PR #84, parent-POM
  1.5.4, two folded work items).
- doc/variants/01-base-gateway.adoc: protocol scope complete (ADR-0002 promise
  closed with WS + gRPC).
- doc/plan/README.adoc, doc/README.adoc: Plan 05 delivered; ADR-0015/0016 rows.
- doc/LogMessages.adoc: doc-first WS LogRecords (INFO relay established / idle
  reclaim; WARN Origin rejection), %s-only templates in range.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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