docs(tasks): decompose review 001 remediation Units 1-5 into taskgraph tasks
21 review-001 tasks across server/adapters/client/gateway/websocket/infra, chunked from the 7-unit remediation plan in docs/reviews/001-initial-implementation-review.md. - Scope split by mechanism, not one-per-finding: 15 tasks in generation 1 (parallelizable), 6 sequenced after their file-sharing precursors - Deliberately deferred until dependent fixes land: projection/doc fidelity partial (Unit 6 beyond dependency hygiene), coverage backfills (COV-01..07 via in-task acceptance for forward.rs), and per-finding minors (OAI-06/07, HY-02/04/06/10/11, CON-08) - Cross-crate WS-12 (alkcall demux 4 GiB discard alloc) noted for filing in alkcall, not here taskgraph: validate clean, no cycles, 6 generations
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: review-001-forward-url-safety
|
||||
name: Safe outbound URL construction — encoding, base-path, host validation (FWD-01, FWD-02)
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: high
|
||||
impact: component
|
||||
level: implementation
|
||||
tags: [adapters, review-001, security]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Review 001 findings FWD-01 + FWD-02, both empirically verified against
|
||||
`src/adapters/forward.rs` and both security-bearing **with the
|
||||
operation's injected credentials attached**:
|
||||
|
||||
- **FWD-01**: path-parameter values are substituted raw
|
||||
(`forward.rs:56-71, 133-141`). `Url::join` normalizes `..`, so
|
||||
`{owner} = "../../admin"` escapes a path-scoped prefix (cross-tenant
|
||||
IDOR); `?` and `#` in a value split/inject into the URL; values
|
||||
containing a later placeholder get re-expanded by the iterative
|
||||
substitution. Fix: percent-encode each segment
|
||||
(`utf8_percent_encode` with a path-segment set), reject/encode
|
||||
`?`/`#`, and render the template in a single pass (not iterative
|
||||
replace). The query path is already correctly encoded via
|
||||
`query_pairs_mut` — keep that shape.
|
||||
- **FWD-02**: `Url::join` resolves against the base *directory*, so
|
||||
`base_url = "https://api.openai.com/v1"` + `/chat/completions` silently
|
||||
drops `/v1` (`forward.rs:74-78`); every test uses origin-only base
|
||||
URLs so the suite can't see it. Worse: a path key that is an absolute
|
||||
URL replaces scheme+host entirely (verified — reqwest only rejects
|
||||
non-http(s) *schemes*), and credential injection happens after URL
|
||||
construction, so a spec-controlled absolute path sends the namespace's
|
||||
credentials to an arbitrary host (SSRF). Specs are
|
||||
assembly-layer-supplied (trusted per ADR-066) but nothing enforces that
|
||||
boundary. Fix: append to the base *path* (not origin), require the
|
||||
joined URL to keep the base host (fail loudly on host change), and
|
||||
require https by default (explicit opt-out for http).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Traversal test: `{owner} = "../../admin"` cannot escape the template path (segment encoded or rejected)
|
||||
- [ ] `?`/`#`/later-placeholder-in-value tested (encoded or rejected, never URL-structural)
|
||||
- [ ] Base URL with a path prefix keeps the prefix (`…/v1` + `/chat/completions` → `…/v1/chat/completions`, test)
|
||||
- [ ] Absolute-URL path template is rejected loudly; joined host ≠ base host is rejected; http base refused unless opted out (tests)
|
||||
- [ ] Rendering is single-pass (a rendered value is never re-substituted)
|
||||
- [ ] Both `from_openapi` and `from_jsonschema` paths covered (they share `forward.rs`)
|
||||
- [ ] `cargo test` and `cargo clippy --all-targets -- -D warnings` pass
|
||||
|
||||
## References
|
||||
|
||||
- docs/reviews/001-initial-implementation-review.md (Part D, FWD-01, FWD-02)
|
||||
- docs/architecture/decisions/066-from-jsonschema-as-http-adapter.md
|
||||
|
||||
## Notes
|
||||
|
||||
> Agent fills during implementation. One of the two gate tasks for
|
||||
> any deployment-facing milestone (with review-001-client-timeout-retry).
|
||||
|
||||
## Summary
|
||||
|
||||
> Filled on completion.
|
||||
Reference in New Issue
Block a user