Decomposition of docs/reviews/002-post-remediation-review.md per its 5-unit remediation plan: - Unit 1 (security-critical): gw15-publish-body-cap, prj16-schema-via-call (CF-004 filed alkcall-side), fwd13-dot-segments, fwd16-missing-capability, oai11-ref-memoization - Unit 2 (timeout/terminality): ws13-idle-progress, fwd15-stream-timeout, cli01-retry-after-budget, con17-mcp-pagination, con18-wss-sweep-exit - Unit 3 (projection/docs): projection-truthfulness, mcp-batch-cap, gw16-status-drift - Unit 4 (spec-import): yaml-normalization, oai13-path-item-wildcards, import-loudness-cluster, js01-placeholder-check, fwd17-19-contract-decisions - Unit 5 (WS polish + tests): con18b-ws-polish, client-policy-wire-tests, cov-deployment-knobs, cov13-dead-code, srv11-srv12-router-ordering - review-002-bracketed-followup: tentatively planned post-bulk pass (stale-check, OQA-18 enforcement decision, CON-08/09 close() lever, cross-crate re-checks) — deliberately not serialized against the bulk Also: review-002 numbering repair (CON-14 was double-booked; MCP pagination now CON-14, from_wss monitor renumbered CON-18, missing CON-14 section added). taskgraph: 66 valid, no cycles; 24 pending (all review-002); gen-1/gen-2 parallel waves identified; workflow-cost hotspots are prj16 (12.8) and ws13 (11.1), both carrying the reviewed slicing guidance in their Notes.
2.9 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| review-002-fwd16-missing-capability | Loud error on missing capability instead of silent unauthenticated request (FWD-16) | pending | narrow | low | component | implementation |
|
Description
Review 002 FWD-16 [major]. The FWD-08 remediation made every
malformed credential fail loudly, but the missing-capability arm
stays silent: forward.rs:200-238 —
if let Some(scheme) = auth_scheme {
if let Some(secret) = context.capabilities.get(namespace) {
// ... four loud arms for malformed names/values
}
// capability == None → falls through: the request is sent
// with NO credential and NO diagnostic
}
Capabilities::get keys on api_key:/http_token: prefixes
(alkcall core/types.rs:97-107), so an assembly layer that registered
the secret under a wrong key form (or forgot) produces corrupted
upstream 401s at call time with zero local diagnostics. This is the
most probable misconfiguration shape and the one inconsistent arm in
an otherwise-loud match — every other arm was made loud by review-001's
FWD-08 fix (tested at forward.rs:1646-1736).
Acceptance Criteria
- When
auth_scheme.is_some()and the capability is absent, the request is NOT sent; the caller gets anINTERNAL-family error naming the missing capability key (e.g. "capabilityhttp_token:{namespace}is absent; refusing to send the request unauthenticated") — message contains no secret material - When
auth_scheme.is_none()the behavior is unchanged (unauthenticated ops stay unauthenticated) - Wire test: authed op, empty capabilities → error envelope (and
the test asserts via the existing
spawn_responder-style seam that the upstream received zero requests) - Existing FWD-08 loud-arm tests unchanged and passing
- Doc note in the module doc: the loud-missing matrix now covers malformed name/value AND absent capability
cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --checkpass
References
- docs/reviews/002-post-remediation-review.md (Part D', FWD-16)
- src/adapters/forward.rs:200-238 (the silent arm), :1646-1736 (the FWD-08 test family to mirror)
- docs/architecture/decisions/014-secret-material-flow-and-capability-injection.md
- tasks/adapters/review-001-forward-url-safety.md (FWD-08's loud-error work this completes)
Notes
Error class choice: INTERNAL (misconfiguration, not caller fault) —
matches the FWD-08 precedent. Non-retryable, since re-sending cannot
succeed without the assembly layer changing. If a deployment legitimately
runs authed-ops with optionally-present credentials (none known
today), that would be a new config flag — do not preemptively add one;
the invariant (ADR-014: credentials flow only via Capabilities) argues
loud-and-closed.